From f5cb08af0fd73baf0a48e4497512389f795f81ac Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Thu, 3 Jul 2025 11:31:14 -0700 Subject: [PATCH 01/20] Fixes payload --- .../FaultInjectionRuleProcessor.cs | 3 +- ...FaultInjectionServerErrorResultInternal.cs | 196 +++++++++++++----- 2 files changed, 146 insertions(+), 53 deletions(-) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs index 6fa1c6e56d..37aa3e1a73 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionRuleProcessor.cs @@ -189,7 +189,8 @@ await BackoffRetryUtility>.ExecuteAsync( result.GetDelay(), result.GetSuppressServiceRequests(), result.GetInjectionRate(), - this.applicationContext)); + this.applicationContext, + this.globalEndpointManager)); } private async Task GetEffectiveConnectionErrorRule(FaultInjectionRule rule) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs index 6cac1ccbd1..067ef57179 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs @@ -8,6 +8,8 @@ namespace Microsoft.Azure.Cosmos.FaultInjection using System.Net; using System.Net.Http.Headers; using System.Text; + using System.Text.RegularExpressions; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; using Microsoft.Azure.Documents.Rntbd; @@ -23,6 +25,7 @@ internal class FaultInjectionServerErrorResultInternal private readonly bool suppressServiceRequest; private readonly double injectionRate; private readonly FaultInjectionApplicationContext applicationContext; + private readonly GlobalEndpointManager globalEndpointManager; /// /// Constructor for FaultInjectionServerErrorResultInternal @@ -32,13 +35,15 @@ internal class FaultInjectionServerErrorResultInternal /// /// /// + /// public FaultInjectionServerErrorResultInternal( FaultInjectionServerErrorType serverErrorType, int times, TimeSpan delay, bool suppressServiceRequest, double injectionRate, - FaultInjectionApplicationContext applicationContext) + FaultInjectionApplicationContext applicationContext, + GlobalEndpointManager globalEndpointManager) { this.serverErrorType = serverErrorType; this.times = times; @@ -46,6 +51,7 @@ public FaultInjectionServerErrorResultInternal( this.suppressServiceRequest = suppressServiceRequest; this.injectionRate = injectionRate; this.applicationContext = applicationContext; + this.globalEndpointManager = globalEndpointManager; } /// @@ -284,6 +290,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st //Global or Local lsn? string lsn = dsr.RequestContext.QuorumSelectedLSN.ToString(CultureInfo.InvariantCulture); INameValueCollection headers = dsr.Headers; + bool isProxyCall = this.IsProxyCall(dsr); switch (this.serverErrorType) { @@ -291,16 +298,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Gone, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.ServerGenerated410)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Gone, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -312,17 +322,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.TooManyRequests, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: TooManyRequests, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.TooManyRequests, SubStatusCodes.RUBudgetExceeded)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: TooManyRequests, rule: {ruleId}"))), }; - - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromMilliseconds(500)); httpResponse.Headers.Add( @@ -336,16 +348,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.RequestTimeout, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Timeout, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.RequestTimeout, SubStatusCodes.Unknown)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Timeout, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -358,16 +373,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.InternalServerError, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Internal Server Error, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.InternalServerError, SubStatusCodes.Unknown)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Internal Server Error, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -381,16 +399,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st const string badSesstionToken = "1:1#1#1=1#1=1"; httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.NotFound, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Read Session Not Available, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.NotFound, SubStatusCodes.ReadSessionNotAvailable)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Read Session Not Available, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -404,16 +425,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsMigrating, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.CompletingPartitionMigration)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsMigrating, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -426,16 +450,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsSplitting, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.CompletingSplit)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsSplitting, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -448,16 +475,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.ServiceUnavailable, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Service Unavailable, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.ServiceUnavailable, SubStatusCodes.Unknown)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Service Unavailable, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -470,16 +500,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Forbidden, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Forbidden, SubStatusCodes.DatabaseAccountNotFound)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -492,16 +525,19 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st httpResponse = new HttpResponseMessage { + Version = isProxyCall + ? new Version(2, 0) + : new Version(1, 1), StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: LeaseNotFound, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytesFromHexString( + FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.LeaseNotFound)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: LeaseNotFound, rule: {ruleId}"))), }; - foreach (string header in headers.AllKeys()) - { - httpResponse.Headers.Add(header, headers.Get(header)); - } + this.SetHttpHeaders(httpResponse, headers); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -515,6 +551,34 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st } } + private bool IsProxyCall(DocumentServiceRequest dsr) + { + string gwUriString = dsr.Headers.Get("FAULTINJECTION_GW_URI"); + + if (!string.IsNullOrEmpty(gwUriString)) + { + Uri proxyUri = this.globalEndpointManager.ResolveThinClientEndpoint(dsr); + Uri requestUri = new Uri(gwUriString); + + return requestUri.Host == proxyUri.Host && + requestUri.Port == proxyUri.Port; + } + + return false; + } + private void SetHttpHeaders( + HttpResponseMessage httpResponse, + INameValueCollection headers) + { + foreach (string header in headers.AllKeys()) + { + if (header != "FAULTINJECTION_GW_URI") + { + httpResponse.Headers.Add(header, headers.Get(header)); + } + } + } + internal class FauntInjectionHttpContent : HttpContent { private readonly Stream content; @@ -534,6 +598,29 @@ protected override bool TryComputeLength(out long length) length = this.content.Length; return true; } + + internal static string GetProxyResponseString(StatusCodes statusCode, SubStatusCodes subStatusCode) + { + Console.WriteLine(ConvertStatusCodeToReversedSwappedHex(503)); + return $"2a000000{ConvertStatusCodeToReversedSwappedHex((int)statusCode)}0000000000000000000000000000000035000201000000000000011c0002{ConvertStatusCodeToReversedSwappedHex((int)subStatusCode)}480000007b22636f6465223a2022343039222c226d657373616765223a2022416e206572726f72206f63637572726564207768696c6520726f7574696e67207468652072657175657374227d"; + } + + private static string ConvertStatusCodeToReversedSwappedHex(int statusCode) + { + // Convert to 8-digit hex, upper-case, padded with zeros + string hex = statusCode.ToString("X8"); + + // Reverse the string + char[] reversed = hex.Reverse().ToArray(); + string reversedHex = new string(reversed); + + // Swap every two digits + // e.g. "12345678" -> "21436587" + string swapped = Regex.Replace(reversedHex, @"(.)(.)", "$2$1"); + + return swapped; + } + } internal static class FaultInjectionResponseEncoding @@ -544,6 +631,11 @@ public static byte[] GetBytes(string value) { return Encoding.GetBytes(value); } + + public static byte[] GetBytesFromHexString(string hexString) + { + return Convert.FromHexString(hexString); + } } } } From bdec17e874260f262076605817c3105fa06e6f9f Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:01:43 -0700 Subject: [PATCH 02/20] New approach --- ...FaultInjectionServerErrorResultInternal.cs | 181 ++++++++---------- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 3 +- .../src/HttpClient/CosmosHttpClientCore.cs | 7 - .../src/ThinClientStoreClient.cs | 26 ++- .../src/ThinClientStoreModel.cs | 7 +- 5 files changed, 110 insertions(+), 114 deletions(-) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs index 067ef57179..aa043403f8 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs @@ -8,7 +8,6 @@ namespace Microsoft.Azure.Cosmos.FaultInjection using System.Net; using System.Net.Http.Headers; using System.Text; - using System.Text.RegularExpressions; using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; @@ -37,9 +36,9 @@ internal class FaultInjectionServerErrorResultInternal /// /// public FaultInjectionServerErrorResultInternal( - FaultInjectionServerErrorType serverErrorType, - int times, - TimeSpan delay, + FaultInjectionServerErrorType serverErrorType, + int times, + TimeSpan delay, bool suppressServiceRequest, double injectionRate, FaultInjectionApplicationContext applicationContext, @@ -170,7 +169,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = retryWithHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Retry With, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.TooManyRequests: @@ -211,7 +210,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = internalServerErrorHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Internal Server Error, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.ReadSessionNotAvailable: @@ -229,7 +228,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = readSessionHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Read Session Not Available, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.PartitionIsMigrating: @@ -243,7 +242,7 @@ public StoreResponse GetInjectedServerError(ChannelCallArguments args, string ru Headers = partitionMigrationHeaders, ResponseBody = new MemoryStream(FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Partition Migrating, rule: {ruleId}")) }; - + return storeResponse; case FaultInjectionServerErrorType.PartitionIsSplitting: @@ -295,7 +294,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st switch (this.serverErrorType) { case FaultInjectionServerErrorType.Gone: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -304,13 +303,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.ServerGenerated410)) + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.ServerGenerated410, "Gone", ruleId)) : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Gone, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -319,7 +318,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.TooManyRequests: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -328,24 +327,24 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.TooManyRequests, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.TooManyRequests, SubStatusCodes.RUBudgetExceeded)) - : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: TooManyRequests, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.TooManyRequests, (int)SubStatusCodes.RUBudgetExceeded, "TooManyRequests", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: TooManyRequests, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromMilliseconds(500)); httpResponse.Headers.Add( - WFConstants.BackendHeaders.SubStatus, + WFConstants.BackendHeaders.SubStatus, ((int)SubStatusCodes.RUBudgetExceeded).ToString(CultureInfo.InvariantCulture)); httpResponse.Headers.Add(WFConstants.BackendHeaders.LocalLSN, lsn); return httpResponse; case FaultInjectionServerErrorType.Timeout: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -354,13 +353,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.RequestTimeout, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.RequestTimeout, SubStatusCodes.Unknown)) + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.RequestTimeout, (int)SubStatusCodes.Unknown, "Timeout", ruleId)) : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Timeout, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -370,7 +369,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.InternalServerError: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -379,13 +378,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.InternalServerError, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.InternalServerError, SubStatusCodes.Unknown)) - : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Internal Server Error, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.InternalServerError, (int)SubStatusCodes.Unknown, "InternalServerError", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: InternalServerError, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -395,7 +394,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.ReadSessionNotAvailable: - + const string badSesstionToken = "1:1#1#1=1#1=1"; httpResponse = new HttpResponseMessage { @@ -405,13 +404,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.NotFound, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.NotFound, SubStatusCodes.ReadSessionNotAvailable)) - : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Read Session Not Available, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.NotFound, (int)SubStatusCodes.ReadSessionNotAvailable, "ReadSessionNotAvailable", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: ReadSessionNotAvailable, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -422,7 +421,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.PartitionIsMigrating: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -431,13 +430,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.CompletingPartitionMigration)) + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.CompletingPartitionMigration, "PartitionIsMigrating", ruleId)) : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsMigrating, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -447,7 +446,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.PartitionIsSplitting: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -456,13 +455,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.CompletingSplit)) + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.CompletingSplit, "PartitionIsSplitting", ruleId)) : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: PartitionIsSplitting, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -481,13 +480,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.ServiceUnavailable, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.ServiceUnavailable, SubStatusCodes.Unknown)) - : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: Service Unavailable, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.ServiceUnavailable, (int)SubStatusCodes.Unknown, "ServiceUnavailable", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: ServiceUnavailable, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -497,7 +496,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st return httpResponse; case FaultInjectionServerErrorType.DatabaseAccountNotFound: - + httpResponse = new HttpResponseMessage { Version = isProxyCall @@ -506,13 +505,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.Forbidden, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Forbidden, SubStatusCodes.DatabaseAccountNotFound)) - : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Forbidden, (int)SubStatusCodes.DatabaseAccountNotFound, "DatabaseAccountNotFound", ruleId)) + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -531,13 +530,13 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st StatusCode = HttpStatusCode.Gone, Content = new FauntInjectionHttpContent( new MemoryStream( - isProxyCall - ? FaultInjectionResponseEncoding.GetBytesFromHexString( - FauntInjectionHttpContent.GetProxyResponseString(StatusCodes.Gone, SubStatusCodes.LeaseNotFound)) + isProxyCall + ? FaultInjectionResponseEncoding.GetBytes( + GetProxyResponseMessageString((int)StatusCodes.Gone, (int)SubStatusCodes.LeaseNotFound, "LeaseNotFound", ruleId)) : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: LeaseNotFound, rule: {ruleId}"))), }; - this.SetHttpHeaders(httpResponse, headers); + this.SetHttpHeaders(httpResponse, headers, isProxyCall); httpResponse.Headers.Add( WFConstants.BackendHeaders.SubStatus, @@ -553,30 +552,37 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st private bool IsProxyCall(DocumentServiceRequest dsr) { - string gwUriString = dsr.Headers.Get("FAULTINJECTION_GW_URI"); - - if (!string.IsNullOrEmpty(gwUriString)) - { - Uri proxyUri = this.globalEndpointManager.ResolveThinClientEndpoint(dsr); - Uri requestUri = new Uri(gwUriString); - - return requestUri.Host == proxyUri.Host && - requestUri.Port == proxyUri.Port; - } + string gwUriString = dsr.Headers.Get("FAULTINJECTION_IS_PROXY"); - return false; + return !string.IsNullOrEmpty(gwUriString); } + private void SetHttpHeaders( HttpResponseMessage httpResponse, - INameValueCollection headers) + INameValueCollection headers, + bool isProxyCall) { foreach (string header in headers.AllKeys()) { - if (header != "FAULTINJECTION_GW_URI") + if (header != "FAULTINJECTION_IS_PROXY") { httpResponse.Headers.Add(header, headers.Get(header)); } } + + if (isProxyCall) + { + httpResponse.Headers.Add(ThinClientConstants.RoutedViaProxy,"1"); + } + } + + private static string GetProxyResponseMessageString( + int statusCode, + int subStatusCode, + string message, + string faultInjectionRuleId) + { + return $"{{\"code\": \"{statusCode}:{subStatusCode}\",\"message\":\"Fault Injection Server Error: {message}, rule: {faultInjectionRuleId}\"}}"; } internal class FauntInjectionHttpContent : HttpContent @@ -598,32 +604,9 @@ protected override bool TryComputeLength(out long length) length = this.content.Length; return true; } - - internal static string GetProxyResponseString(StatusCodes statusCode, SubStatusCodes subStatusCode) - { - Console.WriteLine(ConvertStatusCodeToReversedSwappedHex(503)); - return $"2a000000{ConvertStatusCodeToReversedSwappedHex((int)statusCode)}0000000000000000000000000000000035000201000000000000011c0002{ConvertStatusCodeToReversedSwappedHex((int)subStatusCode)}480000007b22636f6465223a2022343039222c226d657373616765223a2022416e206572726f72206f63637572726564207768696c6520726f7574696e67207468652072657175657374227d"; - } - - private static string ConvertStatusCodeToReversedSwappedHex(int statusCode) - { - // Convert to 8-digit hex, upper-case, padded with zeros - string hex = statusCode.ToString("X8"); - - // Reverse the string - char[] reversed = hex.Reverse().ToArray(); - string reversedHex = new string(reversed); - - // Swap every two digits - // e.g. "12345678" -> "21436587" - string swapped = Regex.Replace(reversedHex, @"(.)(.)", "$2$1"); - - return swapped; - } - } - internal static class FaultInjectionResponseEncoding + internal static class FaultInjectionResponseEncoding { private static readonly UTF8Encoding Encoding = new UTF8Encoding(false); diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index e2ae2509a9..f166591ab1 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1116,7 +1116,8 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, this.eventSource, this.serializerSettings, - this.httpClient); + this.httpClient, + this.chaosInterceptor); thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs b/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs index 75b42b2bf9..25b46ad0a6 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs @@ -359,7 +359,6 @@ private async Task SendHttpHelperAsync( { if (this.chaosInterceptor != null && documentServiceRequest != null) { - this.SetFaultInjectionHeader(documentServiceRequest, requestMessage); (bool hasFault, HttpResponseMessage fiResponseMessage) = await this.InjectFaultsAsync(cancellationTokenSource, documentServiceRequest, requestMessage); if (hasFault) { @@ -374,7 +373,6 @@ private async Task SendHttpHelperAsync( if (this.chaosInterceptor != null && documentServiceRequest != null) { - this.SetFaultInjectionHeader(documentServiceRequest, requestMessage); CancellationToken fiToken = cancellationTokenSource.Token; fiToken.ThrowIfCancellationRequested(); await this.chaosInterceptor.OnAfterHttpSendAsync(documentServiceRequest, fiToken); @@ -469,11 +467,6 @@ private async Task SendHttpHelperAsync( } } - private void SetFaultInjectionHeader(DocumentServiceRequest documentServiceRequest, HttpRequestMessage requestMessage) - { - documentServiceRequest.Headers.Set("FAULTINJECTION_GW_URI", requestMessage.RequestUri.ToString()); - } - private async Task<(bool, HttpResponseMessage)> InjectFaultsAsync( CancellationTokenSource cancellationTokenSource, DocumentServiceRequest documentServiceRequest, diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index f1a82880d0..a6e895b50f 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -13,7 +13,8 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.FaultInjection; using Newtonsoft.Json; using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; @@ -23,17 +24,20 @@ namespace Microsoft.Azure.Cosmos /// internal class ThinClientStoreClient : GatewayStoreClient { - private readonly ObjectPool bufferProviderWrapperPool; + private readonly ObjectPool bufferProviderWrapperPool; + private readonly IChaosInterceptor chaosInterceptor; public ThinClientStoreClient( CosmosHttpClient httpClient, ICommunicationEventSource eventSource, - JsonSerializerSettings serializerSettings = null) + JsonSerializerSettings serializerSettings = null, + IChaosInterceptor chaosInterceptor = null) : base(httpClient, eventSource, serializerSettings) { - this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + this.chaosInterceptor = chaosInterceptor; } public override async Task InvokeAsync( @@ -53,7 +57,19 @@ public override async Task InvokeAsync( globalDatabaseAccountName, clientCollectionCache, cancellationToken)) - { + { + if (this.chaosInterceptor != null) + { + request.Headers.Set("FAULTINJECTION_IS_PROXY", "true"); + (bool hasFault, HttpResponseMessage fiResponseMessage) = await this.chaosInterceptor.OnHttpRequestCallAsync(request, cancellationToken); + if (hasFault) + { + DefaultTrace.TraceInformation("Chaos interceptor injected fault for request: {0}", request); + fiResponseMessage.RequestMessage = responseMessage.RequestMessage; + request.Headers.Remove("FAULTINJECTION_IS_PROXY"); + return await ThinClientStoreClient.ParseResponseAsync(fiResponseMessage, request.SerializerSettings ?? base.SerializerSettings, request); + } + } HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); } diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs index 2aa8f3df21..ee2edc73af 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs @@ -11,6 +11,7 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.FaultInjection; using Newtonsoft.Json; /// @@ -28,7 +29,8 @@ public ThinClientStoreModel( ConsistencyLevel defaultConsistencyLevel, DocumentClientEventSource eventSource, JsonSerializerSettings serializerSettings, - CosmosHttpClient httpClient) + CosmosHttpClient httpClient, + IChaosInterceptor chaosInterceptor) : base(endpointManager, sessionContainer, defaultConsistencyLevel, @@ -40,7 +42,8 @@ public ThinClientStoreModel( this.thinClientStoreClient = new ThinClientStoreClient( httpClient, eventSource, - serializerSettings); + serializerSettings, + chaosInterceptor); } public override async Task ProcessMessageAsync( From dd9ba9c82553bfd36f88057fc3bfe099cca84290 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Fri, 18 Jul 2025 09:51:26 -0700 Subject: [PATCH 03/20] Update ThinClientStoreModelTests.cs --- .../ThinClientStoreModelTests.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index 56726374fe..1364409132 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -53,7 +53,8 @@ public void TestInitialize() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null); + httpClient: null, + chaosInterceptor: null); PartitionKeyRangeCache pkRangeCache = (PartitionKeyRangeCache)FormatterServices.GetUninitializedObject(typeof(PartitionKeyRangeCache)); @@ -121,7 +122,8 @@ public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceRespons defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null); + httpClient: null, + chaosInterceptor: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, @@ -202,7 +204,8 @@ public async Task ProcessMessageAsync_WithUnsupportedOperations_ShouldFallbackTo defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: mockCosmosHttpClient.Object); + httpClient: mockCosmosHttpClient.Object, + chaosInterceptor: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, @@ -292,7 +295,8 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null); + httpClient: null, + chaosInterceptor: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, From 5dde83f204a1bff4eb080818aa942a3a8eb57050 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:29:00 -0700 Subject: [PATCH 04/20] Revert "Merge branch 'master' into users/nalutripician/FIThinclientPayloadFix" This reverts commit 65a94aed8a97247a2daf685cf91b559cfd97478f, reversing changes made to dd9ba9c82553bfd36f88057fc3bfe099cca84290. --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 14421 ++++++++-------- .../src/GatewayStoreModel.cs | 2 +- .../GlobalPartitionEndpointManagerCore.cs | 8 +- .../src/ThinClientStoreClient.cs | 339 +- .../src/ThinClientStoreModel.cs | 29 +- .../CosmosItemIntegrationTests.cs | 276 +- .../RegionFailoverTests.cs | 9 - .../ThinClientStoreModelTests.cs | 169 +- 8 files changed, 7393 insertions(+), 7860 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index d07329d231..2a86cfeb92 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1,6850 +1,6849 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Security; - using System.Security; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using global::Azure.Core; - using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Query; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Telemetry; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tracing.TraceData; - using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Client; - using Microsoft.Azure.Documents.Collections; - using Microsoft.Azure.Documents.FaultInjection; - using Microsoft.Azure.Documents.Routing; - using Newtonsoft.Json; - using ResourceType = Documents.ResourceType; - - /// - /// Provides a client-side logical representation for the Azure Cosmos DB service. - /// This client is used to configure and execute requests against the service. - /// - /// - /// This type is thread safe. - /// - /// - /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. - /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. - /// - /// - /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. - /// Depending on your programming technique, you can do this in one of two ways: - /// - /// - /// - /// 1. By using a language construct such as the using statement in C#. - /// The using statement is actually a syntactic convenience. - /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. - /// - /// - /// - /// - /// - /// - /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. - /// The following example replaces the using block in the previous example with a try/catch/finally block. - /// - /// - /// - /// - /// - /// - internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal - { - private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; - private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; - private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; - private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; - private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; - private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; - private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; - private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; - private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; - private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; - private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; - private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; - private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; - private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; - private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; - // Env variable - private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; - - private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; - private const int DefaultMaxRequestsPerRntbdChannel = 30; - private const int DefaultRntbdPartitionCount = 1; - private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; - private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; - private const int DefaultRntbdPortPoolReuseThreshold = 256; - private const int DefaultRntbdPortPoolBindAttempts = 5; - private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; - private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; - private const bool DefaultEnableCpuMonitor = true; - private const string DefaultInitTaskKey = "InitTaskKey"; - - /// - /// Default thresholds for PPAF request hedging. - /// - private const int DefaultHedgingThresholdInMilliseconds = 1000; - private const int DefaultHedgingThresholdStepInMilliseconds = 500; - - private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; - private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; - - private readonly bool IsLocalQuorumConsistency = false; - private readonly bool isReplicaAddressValidationEnabled; - private readonly bool enableAsyncCacheExceptionNoSharing; - - private readonly bool isThinClientEnabled; - - //Fault Injection - private readonly IChaosInterceptorFactory chaosInterceptorFactory; - private readonly IChaosInterceptor chaosInterceptor; - - private bool isChaosInterceptorInititalized = false; - - //Auth - internal readonly AuthorizationTokenProvider cosmosAuthorization; - - // Gateway has backoff/retry logic to hide transient errors. - private RetryPolicy retryPolicy; - private bool allowOverrideStrongerConsistency = false; - private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; - private int openConnectionTimeoutInSeconds = 5; - private int idleConnectionTimeoutInSeconds = -1; - private int timerPoolGranularityInSeconds = 1; - private bool enableRntbdChannel = true; - private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; - private int rntbdPartitionCount = DefaultRntbdPartitionCount; - private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; - private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; - private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; - private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - private bool enableCpuMonitor = DefaultEnableCpuMonitor; - private int rntbdMaxConcurrentOpeningConnectionCount = 5; - private string clientId; - - //Consistency - private Documents.ConsistencyLevel? desiredConsistencyLevel; - - internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } - - internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } - - private ClientCollectionCache collectionCache; - - private PartitionKeyRangeCache partitionKeyRangeCache; - - //Private state. - private bool isSuccessfullyInitialized; - private bool isDisposed; - - // creator of TransportClient is responsible for disposing it. - private IStoreClientFactory storeClientFactory; - internal CosmosHttpClient httpClient { get; private set; } - - // Flag that indicates whether store client factory must be disposed whenever client is disposed. - // Setting this flag to false will result in store client factory not being disposed when client is disposed. - // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. - private bool isStoreClientFactoryCreatedInternally; - - //Id counter. - private static int idCounter; - //Trace Id. - private int traceId; - - //RemoteCertificateValidationCallback - internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; - - //Distributed Tracing Flag - internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; - - //SessionContainer. - internal ISessionContainer sessionContainer; - - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - - private AsyncLazy queryPartitionProvider; - - private DocumentClientEventSource eventSource; - private Func> initializeTaskFactory; - internal AsyncCacheNonBlocking initTaskCache; - - private JsonSerializerSettings serializerSettings; - private event EventHandler sendingRequest; - private event EventHandler receivedResponse; - private Func transportClientHandlerFactory; - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - { - if (authKey == null) - { - throw new ArgumentNullException("authKey"); - } - - if (authKey != null) - { - this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); - } - - this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); - this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The connection policy for the client. - /// - /// - /// This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - HttpMessageHandler handler, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) - { - } - - internal DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null) - : this(serviceEndpoint, - AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), - sendingRequestEventArgs, - connectionPolicy, - desiredConsistencyLevel, - serializerSettings, - apitype, - receivedResponseEventArgs, - handler, - sessionContainer, - enableCpuMonitor, - transportClientHandlerFactory, - storeClientFactory) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The cosmos authorization for the client. - /// The event handler to be invoked before the request is sent. - /// The event handler to be invoked after a response has been received. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// Api type for the account - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// The default session container with which DocumentClient is created. - /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. - /// Transport client handler factory. - /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. - /// Flag to allow Quorum Read with Eventual Consistency Account - /// - /// This delegate responsible for validating the third party certificate. - /// This is distributed tracing flag - /// This is the chaos interceptor used for fault injection - /// A boolean flag indicating if stack trace optimization is enabled. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - internal DocumentClient(Uri serviceEndpoint, - AuthorizationTokenProvider cosmosAuthorization, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null, - bool isLocalQuorumConsistency = false, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - IChaosInterceptorFactory chaosInterceptorFactory = null, - bool enableAsyncCacheExceptionNoSharing = true) - { - if (sendingRequestEventArgs != null) - { - this.sendingRequest += sendingRequestEventArgs; - } - - if (serializerSettings != null) - { - this.serializerSettings = serializerSettings; - } - - this.ApiType = apitype; - - if (receivedResponseEventArgs != null) - { - this.receivedResponse += receivedResponseEventArgs; - } - - this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; - this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); - this.transportClientHandlerFactory = transportClientHandlerFactory; - this.IsLocalQuorumConsistency = isLocalQuorumConsistency; - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.chaosInterceptorFactory = chaosInterceptorFactory; - this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); - this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - - this.Initialize( - serviceEndpoint: serviceEndpoint, - connectionPolicy: connectionPolicy, - desiredConsistencyLevel: desiredConsistencyLevel, - handler: handler, - sessionContainer: sessionContainer, - enableCpuMonitor: enableCpuMonitor, - storeClientFactory: storeClientFactory, - cosmosClientId: cosmosClientId, - remoteCertificateValidationCallback: remoteCertificateValidationCallback, - cosmosClientTelemetryOptions: cosmosClientTelemetryOptions, - enableThinClientMode: this.isThinClientEnabled); - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The connection policy for the client. - /// The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Internal constructor purely for unit-testing - /// - internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) - { - // do nothing - this.ServiceEndpoint = serviceEndpoint; - this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); - } - - internal virtual async Task GetCollectionCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.collectionCache; - } - } - - internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.partitionKeyRangeCache; - } - } - - internal GlobalAddressResolver AddressResolver { get; private set; } - - internal GlobalEndpointManager GlobalEndpointManager { get; private set; } - - internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } - - /// - /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. - /// - /// - /// A object. - /// - /// - /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. - /// If there are any initialization exceptions, this method will throw them (set on the task). - /// Alternately, calling any API will throw initialization exception at the first call. - /// - /// - /// - /// - /// - /// - public Task OpenAsync(CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); - } - - private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); - } - - private async Task OpenPrivateAsync(CancellationToken cancellationToken) - { - // Initialize caches for all databases and collections - ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( - new FeedOptions { MaxItemCount = -1 }); - - try - { - while (databaseFeedReader.HasMoreResults) - { - foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) - { - ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( - database.SelfLink, - new FeedOptions { MaxItemCount = -1 }); - List tasks = new List(); - while (collectionFeedReader.HasMoreResults) - { - tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); - } - - await Task.WhenAll(tasks); - } - } - } - catch (DocumentClientException ex) - { - // Clear the caches to ensure that we don't have partial results - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - - DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); - } - } - - internal virtual void Initialize(Uri serviceEndpoint, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - IStoreClientFactory storeClientFactory = null, - TokenCredential tokenCredential = null, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - bool enableThinClientMode = false) - { - if (serviceEndpoint == null) - { - throw new ArgumentNullException("serviceEndpoint"); - } - - this.clientId = cosmosClientId; - this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; - this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); - - this.queryPartitionProvider = new AsyncLazy(async () => - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); - }, CancellationToken.None); - -#if !(NETSTANDARD15 || NETSTANDARD16) -#if NETSTANDARD20 - // GetEntryAssembly returns null when loaded from native netstandard2.0 - if (System.Reflection.Assembly.GetEntryAssembly() != null) - { -#endif - // For tests we want to allow stronger consistency during construction or per call - string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; - if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) - { - if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) - { - this.allowOverrideStrongerConsistency = false; - } - } - - // We might want to override the defaults sometime - string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; - if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) - { - int maxConcurrentConnectionOpenRequestOverrideInt = 0; - if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) - { - this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; - } - } - - string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) - { - int openConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) - { - this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; - } - } - - string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) - { - int idleConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) - { - this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; - } - } - - string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; - if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) - { - int timerPoolGranularityInSecondsOverrideInt = 0; - if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) - { - // timeoutgranularity specified should be greater than min(5 seconds) - if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) - { - this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; - } - } - } - - string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; - if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) - { - bool enableRntbdChannel = false; - if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) - { - this.enableRntbdChannel = enableRntbdChannel; - } - } - - string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; - if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) - { - int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; - if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) - { - this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; - } - } - - string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; - if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) - { - int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; - if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) - { - this.rntbdPartitionCount = rntbdPartitionCount; - } - } - - string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; - if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) - { - int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) - { - this.maxRntbdChannels = maxRntbdChannels; - } - } - - string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; - if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) - { - PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; - if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) - { - this.rntbdPortReuseMode = portReuseMode; - } - } - - string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; - if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) - { - int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; - if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) - { - this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; - } - } - - string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; - if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) - { - int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; - if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) - { - this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; - } - } - - string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) - { - int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) - { - this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; - } - } - - string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) - { - int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) - { - this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; - } - } - - if (enableCpuMonitor.HasValue) - { - this.enableCpuMonitor = enableCpuMonitor.Value; - } - else - { - string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; - if (!string.IsNullOrEmpty(enableCpuMonitorString)) - { - bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; - if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) - { - this.enableCpuMonitor = enableCpuMonitorFlag; - } - } - } -#if NETSTANDARD20 - } -#endif -#endif - - string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); - if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) - { - if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) - { - if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) - { - throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); - } - - this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; - } - } - - // ConnectionPolicy always overrides appconfig - if (connectionPolicy != null) - { - if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) - { - this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) - { - this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) - { - this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; - } - - if (connectionPolicy.MaxTcpPartitionCount.HasValue) - { - this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; - } - - if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) - { - this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; - } - - if (connectionPolicy.PortReuseMode.HasValue) - { - this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; - } - } - - this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); - - this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; - -#if !NETSTANDARD16 - if (ServicePointAccessor.IsSupported) - { - ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); - servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; - } -#endif - this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); - - this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( - this.ApiType, - DocumentClientEventSource.Instance, - this.ConnectionPolicy, - handler, - this.sendingRequest, - this.receivedResponse, - this.chaosInterceptor); - - // Loading VM Information (non blocking call and initialization won't fail if this call fails) - VmMetadataApiHandler.TryInitialize(this.httpClient); - - if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) - { - CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); - CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); - - CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); - } - - // Starting ClientTelemetry Job - this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, - this.ConnectionPolicy, - this.cosmosAuthorization, - this.httpClient, - this.ServiceEndpoint, - this.GlobalEndpointManager, - this.cancellationTokenSource, - this.chaosInterceptor is not null); - - if (sessionContainer != null) - { - this.sessionContainer = sessionContainer; - } - else - { - this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); - } - - this.desiredConsistencyLevel = desiredConsistencyLevel; - // Setup the proxy to be used based on connection mode. - // For gateway: GatewayProxy. - // For direct: WFStoreProxy [set in OpenAsync()]. - this.eventSource = DocumentClientEventSource.Instance; - - this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( - () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), - new ResourceThrottleRetryPolicy( - this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, - this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); - - // Create the task to start the initialize task - // Task will be awaited on in the EnsureValidClientAsync - Task initTask = this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - - // ContinueWith on the initialization task is needed for handling the UnobservedTaskException - // if this task throws for some reason. Awaiting inside a constructor is not supported and - // even if we had to await inside GetInitializationTask to catch the exception, that will - // be a blocking call. In such cases, the recommended approach is to "handle" the - // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted - // and accessing the Exception property on the target task. -#pragma warning disable VSTHRD110 // Observe result of async calls -#pragma warning disable CDX1000 // DontConvertExceptionToObject - initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); -#pragma warning restore CDX1000 // DontConvertExceptionToObject -#pragma warning restore VSTHRD110 // Observe result of async calls - - this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); - DefaultTrace.TraceInformation(string.Format( - CultureInfo.InvariantCulture, - "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", - this.traceId, - serviceEndpoint.ToString(), - this.ConnectionPolicy.ConnectionMode.ToString(), - this.ConnectionPolicy.ConnectionProtocol.ToString(), - desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); - - this.QueryCompatibilityMode = QueryCompatibilityMode.Default; - } - - // Always called from under the lock except when called from Intilialize method during construction. - private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) - { - await this.InitializeGatewayConfigurationReaderAsync(); - - if (this.desiredConsistencyLevel.HasValue) - { - this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); - } - - if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride - && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) - { - this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; - } - - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; - this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); - this.InitializePartitionLevelFailoverWithDefaultHedging(); - - this.PartitionKeyRangeLocation = - this.ConnectionPolicy.EnablePartitionLevelFailover - || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker - ? new GlobalPartitionEndpointManagerCore( - this.GlobalEndpointManager, - this.ConnectionPolicy.EnablePartitionLevelFailover, - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) - : GlobalPartitionEndpointManagerNoOp.Instance; - - this.retryPolicy = new RetryPolicy( - globalEndpointManager: this.GlobalEndpointManager, - connectionPolicy: this.ConnectionPolicy, - partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation); - - this.ResetSessionTokenRetryPolicy = this.retryPolicy; - - GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( - this.GlobalEndpointManager, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); - - this.GatewayStoreModel = gatewayStoreModel; - - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); - - gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway && this.isThinClientEnabled) - { - ThinClientStoreModel thinClientStoreModel = new ( - endpointManager: this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, - chaosInterceptor: this.chaosInterceptor); - - thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - this.StoreModel = thinClientStoreModel; - } - else if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) - { - this.StoreModel = this.GatewayStoreModel; - } - else - { - this.InitializeDirectConnectivity(storeClientFactory); - } - - return true; - } - - private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) - { - if (databaseName == null) - { - throw new ArgumentNullException(nameof(databaseName)); - } - - if (collection == null) - { - throw new ArgumentNullException(nameof(collection)); - } - - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - using ( - DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Query, - ResourceType.Document, - collection.SelfLink, - AuthorizationTokenType.PrimaryMasterKey)) - { - ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( - resolvedCollection.ResourceId, - new Range( - PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, - PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, - true, - false), - NoOpTrace.Singleton); - - // In Gateway mode, AddressCache is null - if (this.AddressResolver != null) - { - await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); - } - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. - /// - /// - /// - /// The session object used for version tracking when the consistency level is set to Session. - /// - /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. - /// - public object Session - { - get - { - return this.sessionContainer; - } - - set - { - SessionContainer container = value as SessionContainer; - if (container == null) - { - throw new ArgumentNullException("value"); - } - - if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - ClientResources.BadSession, - container.HostName, - this.ServiceEndpoint.Host)); - } - - SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; - if (currentSessionContainer == null) - { - throw new ArgumentNullException(nameof(currentSessionContainer)); - } - - currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. - /// - /// Collection for which session token must be retrieved. - /// - /// The session token used for version tracking when the consistency level is set to Session. - /// - /// - /// The session token can be saved and supplied to a request via . - /// - internal string GetSessionToken(string collectionLink) - { - SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; - - if (sessionContainerInternal == null) - { - throw new ArgumentNullException(nameof(sessionContainerInternal)); - } - - return sessionContainerInternal.GetSessionToken(collectionLink); - } - - /// - /// Gets the Api type - /// - internal ApiType ApiType - { - get; private set; - } - - internal bool UseMultipleWriteLocations { get; private set; } - - /// - /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. - /// - /// - /// The Uri for the service endpoint. - /// - /// - public Uri ServiceEndpoint - { - get; - private set; - } - - /// - /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri WriteEndpoint - { - get - { - return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri ReadEndpoint - { - get - { - return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the Connection policy used by the client from the Azure Cosmos DB service. - /// - /// - /// The Connection policy used by the client. - /// - /// - public ConnectionPolicy ConnectionPolicy { get; private set; } - - /// - /// Gets the AuthKey used by the client from the Azure Cosmos DB service. - /// - /// - /// The AuthKey used by the client. - /// - /// - public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); - - /// - /// Gets the configured consistency level of the client from the Azure Cosmos DB service. - /// - /// - /// The configured of the client. - /// - /// - public virtual Documents.ConsistencyLevel ConsistencyLevel - { - get - { -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); -#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits - return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : - this.accountServiceConfiguration.DefaultConsistencyLevel; - } - } - - /// - /// Returns the account properties available in the service configuration if the client was initialized. - /// - public bool TryGetCachedAccountProperties(out AccountProperties properties) - { - if (this.isSuccessfullyInitialized - && this.accountServiceConfiguration != null - && this.accountServiceConfiguration.AccountProperties != null) - { - properties = this.accountServiceConfiguration.AccountProperties; - return true; - } - - properties = null; - return false; - } - - /// - /// Disposes the client for the Azure Cosmos DB service. - /// - /// - /// - /// - /// - /// - public void Dispose() - { - if (this.isDisposed) - { - return; - } - - if (this.telemetryToServiceHelper != null) - { - this.telemetryToServiceHelper.Dispose(); - this.telemetryToServiceHelper = null; - } - - if (!this.cancellationTokenSource.IsCancellationRequested) - { - this.cancellationTokenSource.Cancel(); - } - - this.cancellationTokenSource.Dispose(); - - if (this.StoreModel != null) - { - this.StoreModel.Dispose(); - this.StoreModel = null; - } - - if (this.storeClientFactory != null) - { - // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference - if (this.isStoreClientFactoryCreatedInternally) - { - this.storeClientFactory.Dispose(); - } - - this.storeClientFactory = null; - } - - if (this.AddressResolver != null) - { - this.AddressResolver.Dispose(); - this.AddressResolver = null; - } - - if (this.httpClient != null) - { - try - { - this.httpClient.Dispose(); - } - catch (Exception exception) - { - DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", - exception.Message); - } - - this.httpClient = null; - } - - if (this.cosmosAuthorization != null) - { - this.cosmosAuthorization.Dispose(); - } - - if (this.GlobalEndpointManager != null) - { - this.GlobalEndpointManager.Dispose(); - this.GlobalEndpointManager = null; - } - - if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) - { - this.queryPartitionProvider.Value.Dispose(); - } - - if (this.initTaskCache != null) - { - this.initTaskCache.Dispose(); - this.initTaskCache = null; - } - - DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); - DefaultTrace.Flush(); - - this.isDisposed = true; - } - - //Compatibility mode: - // Allows to specify compatibility mode used by client when making query requests. - // should be removed when application/sql is no longer supported. - internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } - - /// - /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). - /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache - /// to resolves the request's collection name. If it differs from the session container's resource id it - /// explains the session unavailable exception: somebody removed and recreated the collection. In this - /// case we retry once again (with empty session token) otherwise we return the error to the client - /// (see RenameCollectionAwareClientRetryPolicy) - /// - internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } - - /// - /// Gets and sets the IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension StoreModel { get; set; } - - /// - /// Gets and sets the gateway IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension GatewayStoreModel { get; set; } - - /// - /// Gets and sets on execute scalar query callback - /// - /// - /// Test hook to enable unit test for scalar queries - /// - internal Action OnExecuteScalarQueryCallback { get; set; } - - internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; - - internal virtual async Task GetDefaultConsistencyLevelAsync() - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; - } - - internal Task GetDesiredConsistencyLevelAsync() - { - return Task.FromResult(this.desiredConsistencyLevel); - } - - internal async Task ProcessRequestAsync( - string verb, - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - string testAuthorization = null) // Only for unit-tests - { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (verb == null) - { - throw new ArgumentNullException(nameof(verb)); - } - - (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( - request.ResourceAddress, - PathsHelper.GetResourcePath(request.ResourceType), - verb, - request.Headers, - AuthorizationTokenType.PrimaryMasterKey); - - // Unit-test hook - if (testAuthorization != null) - { - payload = testAuthorization; - authorization = testAuthorization; - } - request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; - - try - { - return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); - } - catch (DocumentClientException dce) - { - this.cosmosAuthorization.TraceUnauthorized( - dce, - authorization, - payload); - - throw; - } - } - - internal Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); - } - - internal async Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - ITrace trace, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(trace); - - retryPolicyInstance?.OnBeforeSendRequest(request); - - using (new ActivityScope(Guid.NewGuid())) - { - IStoreModel storeProxy = this.GetStoreProxy(request); - return await storeProxy.ProcessMessageAsync(request, cancellationToken); - } - } - - /// - /// Establishes and Initializes the Rntbd connection to all the backend replica nodes - /// for the given database name and container. - /// - /// A string containing the cosmos database name. - /// A string containing the cosmos container link uri. - /// An instance of the . - internal async Task OpenConnectionsToAllReplicasAsync( - string databaseName, - string containerLinkUri, - CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(databaseName) || - string.IsNullOrEmpty(containerLinkUri)) - { - string resourceName = string.IsNullOrEmpty(databaseName) ? - nameof(databaseName) : - nameof(containerLinkUri); - - throw new ArgumentNullException(resourceName); - } - - if (this.StoreModel != null) - { - try - { - await this.StoreModel.OpenConnectionsToAllReplicasAsync( - databaseName, - containerLinkUri, - cancellationToken); - } - catch (Exception) - { - throw; - } - } - } - - private static string NormalizeAuthorizationPayload(string input) - { - const int expansionBuffer = 12; - StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); - for (int i = 0; i < input.Length; i++) - { - switch (input[i]) - { - case '\n': - builder.Append("\\n"); - break; - case '/': - builder.Append("\\/"); - break; - default: - builder.Append(input[i]); - break; - } - } - - return builder.ToString(); - } - - internal async Task InitilizeFaultInjectionAsync() - { - if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) - { - this.isChaosInterceptorInititalized = true; - await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); - } - } - - internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) - { - return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.maxRequestsPerRntbdChannel, - this.maxRntbdChannels, - this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - this.rntbdPortReuseMode); - } - - internal virtual async Task EnsureValidClientAsync(ITrace trace) - { - if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) - { - return; - } - - // Trace when the Initialization of client has not been completed. Usually during first call - using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) - { - // If the initialization task failed, we should retry initialization. - // We may end up throwing the same exception but this will ensure that we dont have a - // client which is unusable and can resume working if it failed initialization once. - // If we have to reinitialize the client, it needs to happen in thread safe manner so that - // we dont re-initalize the task again for each incoming call. - try - { - this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - } - catch (DocumentClientException ex) - { - throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( - dce: ex, - trace: trace); - } - catch (Exception e) - { - DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); - childTrace.AddDatum("initializeTask failed", e.Message); - throw; - } - - await this.InitilizeFaultInjectionAsync(); - } - } - - #region Create Impl - /// - /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed. - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); - } - - private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, - Documents.Client.RequestOptions options) - { - if (database == null) - { - throw new ArgumentNullException("database"); - } - - // Doing a Read before Create will give us better latency for existing databases - try - { - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDatabaseAsync(database, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - - /// - /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to create. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// Finally, a Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Create a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, - object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, - CancellationToken cancellationToken = default) - { - // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy); - } - - private async Task> CreateDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 collection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDocumentCollectionPrivateAsync( - string databaseLink, - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse( - await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 DocumentCollection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); - } - - private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( - string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) - { - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make - // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. - Documents.Database database = await this.ReadDatabaseAsync(databaseLink); - - // Doing a Read before Create will give us better latency for existing collections. - // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, - // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. - try - { - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - - /// - /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link to the source object. - /// The target object. - /// (optional)The point in time to restore. If null, use the latest restorable time. - /// (Optional) The for the request. - /// The task object representing the service response for the asynchronous operation. - internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - if (targetDocumentCollection == null) - { - throw new ArgumentNullException("targetDocumentCollection"); - } - - bool isFeed; - string resourceTypeString; - string resourceIdOrFullName; - bool isNameBased; - - string dbsId; - string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); - if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - dbsId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - string sourceCollId; - if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - sourceCollId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - this.ValidateResource(targetDocumentCollection); - - if (options == null) - { - options = new Documents.Client.RequestOptions(); - } - if (!options.RemoteStorageType.HasValue) - { - options.RemoteStorageType = RemoteStorageType.Standard; - } - options.SourceDatabaseId = dbsId; - options.SourceCollectionId = sourceCollId; - if (restoreTime.HasValue) - { - options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - targetDocumentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Get the status of a collection being restored in the Azure Cosmos DB service. - /// - /// The link of the document collection being restored. - /// The task object representing the service response for the asynchronous operation. - internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (string.IsNullOrEmpty(targetDocumentCollectionLink)) - { - throw new ArgumentNullException("targetDocumentCollectionLink"); - } - - ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( - targetDocumentCollectionLink, - new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, - retryPolicyInstance); - string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); - if (restoreState == null) - { - restoreState = RestoreState.RestoreCompleted.ToString(); - } - - DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() - { - State = restoreState - }; - - return ret; - } - - /// - /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ - /// The object to create. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the created object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. - /// - /// - /// 409 - /// - /// Conflict - This means a with an id matching the id field of already existed, - /// or there is already a pending snapshot for the specified resource link. - /// - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that - /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (snapshot == null) - { - throw new ArgumentNullException("snapshot"); - } - - this.ValidateResource(snapshot); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Snapshots_Root, - snapshot, - ResourceType.Snapshot, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Delete Impl - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. snapshots/snapshot_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Replace Impl - /// - /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. - /// - /// the updated document collection. - /// the request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceDocumentCollectionPrivateAsync( - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(documentCollection), - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - // set the session token - if (collection.Resource != null) - { - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - } - return collection; - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If either or is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now dynamically cast doc back to your MyPoco - /// MyPoco poco = (dynamic)doc; - /// - /// //Update some properties of the poco object - /// poco.MyProperty = "updated value"; - /// - /// //Now persist these changes to the database using doc.SelLink and the update poco object - /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if ((options == null) || (options.PartitionKey == null)) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible( - () => this.ReplaceDocumentPrivateAsync( - documentLink, - document, - options, - requestRetryPolicy, - cancellationToken), - requestRetryPolicy, - cancellationToken); - } - - private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// doc.SetPropertyValue("MyProperty", "updated value"); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Document updated = await client.ReplaceDocumentAsync(doc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( - this.GetLinkForRouting(document), - document, - options, - retryPolicyInstance, - cancellationToken), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - this.ValidateResource(document); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - documentLink, - document, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, document, options); - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "sproc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// sproc.Body = "function () {new javascript body for sproc}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceStoredProcedurePrivateAsync( - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(storedProcedure), - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "trigger id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// trigger.Body = "function () {new javascript body for trigger}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// Trigger updated = await client.ReplaceTriggerAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(trigger), - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "udf id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// udf.Body = "function () {new javascript body for udf}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedFunctionPrivateAsync( - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(function), - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. - /// - /// - /// - /// - /// - /// r.ResourceLink == "collection selfLink") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Create a new offer with the changed throughput - /// OfferV2 newOffer = new OfferV2(offer, 5000); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Offer updated = await client.ReplaceOfferAsync(newOffer); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceOfferAsync(Offer offer) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (offer == null) - { - throw new ArgumentNullException("offer"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - offer.SelfLink, - offer, - ResourceType.Offer, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse( - await this.UpdateAsync(request, retryPolicyInstance), - OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "user defined type id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(userDefinedType), - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Read Impl - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Database resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// (docLink); - /// ]]> - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the DocumentCollection to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionPrivateAsync( - string documentCollectionLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the stored procedure to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Trigger to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the User Defined Function to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Conflict to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads an from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Offer to be read. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. - /// - /// - /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about - /// minimum throughput of a Cosmos container (or a database) - /// To retrieve the minimum throughput for a collection/database, use the following sample - /// - /// response = await client.ReadOfferAsync(offer.SelfLink); - /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; - /// ]]> - /// - /// - /// - /// - /// - /// - /// - /// - public Task> ReadOfferAsync(string offerLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(offerLink)) - { - throw new ArgumentNullException("offerLink"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Offer, - offerLink, - null, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Reads a as an asynchronous operation. - /// - /// The link for the schema to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Schema are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentSchemaLink)) - { - throw new ArgumentNullException("documentSchemaLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Schema, - documentSchemaLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the UserDefinedType resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypeLink)) - { - throw new ArgumentNullException("userDefinedTypeLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedType, - userDefinedTypeLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Snapshot resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Snapshot are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/snapshots/{snapshot identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region ReadFeed Impl - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDatabaseFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = null; - /// List ids = new List(); - /// do - /// { - /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); - /// foreach (var item in response) - /// { - /// ids.Add(item.Id); - /// } - /// } - /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(partitionKeyRangesLink)) - { - throw new ArgumentNullException("partitionKeyRangesLink"); - } - - return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionsLink)) - { - throw new ArgumentNullException("collectionsLink"); - } - - return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProceduresLink)) - { - throw new ArgumentNullException("storedProceduresLink"); - } - - return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggersLink)) - { - throw new ArgumentNullException("triggersLink"); - } - - return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedFunctionsLink)) - { - throw new ArgumentNullException("userDefinedFunctionsLink"); - } - - return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. - /// This takes returns a which will contain an enumerable list of dynamic objects. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a containing dynamic objects representing the items in the feed. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. - /// This is important becuse a DocumentCollection can contain different kinds of documents. - /// - /// - /// - /// - public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentsLink)) - { - throw new ArgumentNullException("documentsLink"); - } - - DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); - return new DocumentFeedResponse( - response.Cast(), - response.Count, - response.Headers, - response.UseETagAsContinuation, - response.QueryMetrics, - response.RequestStatistics, - responseLengthBytes: response.ResponseLengthBytes); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); - } - - private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictsLink)) - { - throw new ArgumentNullException("conflictsLink"); - } - - return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service - /// as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadOfferAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadOffersFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionSchemaLink)) - { - throw new ArgumentNullException("documentCollectionSchemaLink"); - } - - return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypesLink)) - { - throw new ArgumentNullException("userDefinedTypesLink"); - } - - return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a set of containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadSnapshotFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); - } - - #endregion - - #region Stored procs - /// - /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) - { - return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - default, - procedureParams), - retryPolicyInstance); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - cancellationToken, - procedureParams), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ExecuteStoredProcedurePrivateAsync( - string storedProcedureLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - params dynamic[] procedureParams) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); - string storedProcedureInput = serializerSettings == null ? - JsonConvert.SerializeObject(procedureParams) : - JsonConvert.SerializeObject(procedureParams, serializerSettings); - using (MemoryStream storedProcedureInputStream = new MemoryStream()) - { - using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) - { - await writer.WriteAsync(storedProcedureInput); - await writer.FlushAsync(); - storedProcedureInputStream.Position = 0; - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.ExecuteJavaScript, - ResourceType.StoredProcedure, - storedProcedureLink, - storedProcedureInputStream, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); - if (options?.PartitionKeyRangeId == null) - { - await this.AddPartitionKeyInformationAsync( - request, - options); - } - - retryPolicyInstance?.OnBeforeSendRequest(request); - - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new StoredProcedureResponse(await this.ExecuteProcedureAsync( - request, - retryPolicyInstance, - cancellationToken), - this.GetSerializerSettingsForRequest(options)); - } - } - } - } - - #endregion - - #region Upsert Impl - /// - /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to upsert. - /// (Optional) The for the request. - /// The that was upserted within a task object representing the service response for the asynchronous operation. - /// If is not set - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// - /// - /// The example below upserts a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to upsert. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// A Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Upsert a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) - { - // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy, cancellationToken); - } - - private async Task> UpsertDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - // To be implemented. - throw new NotImplementedException(); - } - - /// - /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. - /// - /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ - /// The object to upsert. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the upserted object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - #endregion - - #region IAuthorizationTokenProvider - - ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType) - { - return this.cosmosAuthorization.GetUserAuthorizationAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType); - } - - ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType, - ITrace trace) - { - return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType, - trace); - } - - Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( - DocumentServiceRequest request, - string federationId, - string verb, - string resourceId) - { - return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( - request, - federationId, - verb, - resourceId); - } - - #endregion - - #region Core Implementation - internal Task CreateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpdateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); - } - - internal Task ReadAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task ReadFeedAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task DeleteAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteProcedureAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteQueryAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpsertAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - #endregion - - /// - /// Read the from the Azure Cosmos DB service as an asynchronous operation. - /// - /// - /// A wrapped in a object. - /// - public Task GetDatabaseAccountAsync() - { - return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); - } - - /// - /// Read the as an asynchronous operation - /// given a specific reginal endpoint url. - /// - /// The reginal url of the serice endpoint. - /// The CancellationToken - /// - /// A wrapped in a object. - /// - Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) - { - return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); - } - - private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) - { - async ValueTask CreateRequestMessage() - { - HttpRequestMessage request = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = serviceEndpoint - }; - - INameValueCollection headersCollection = new StoreResponseNameValueCollection(); - await this.cosmosAuthorization.AddAuthorizationHeaderAsync( - headersCollection, - serviceEndpoint, - "GET", - AuthorizationTokenType.PrimaryMasterKey); - - foreach (string key in headersCollection.AllKeys()) - { - request.Headers.Add(key, headersCollection[key]); - } - - return request; - } - - AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, - clientSideRequestStatistics: null); - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; - - if (this.queryPartitionProvider.IsValueCreated) - { - (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); - } - - return databaseAccount; - } - - return null; - } - - #region Private Impl - - /// - /// Certain requests must be routed through gateway even when the client connectivity mode is direct. - /// For e.g., DocumentCollection creation. This method returns the based - /// on the input . - /// - /// Returns to which the request must be sent - internal IStoreModel GetStoreProxy(DocumentServiceRequest request) - { - // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) - // we return the Gateway store model - if (request.UseGatewayMode) - { - return this.GatewayStoreModel; - } - - ResourceType resourceType = request.ResourceType; - OperationType operationType = request.OperationType; - - if (resourceType == ResourceType.Offer || - (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || - resourceType == ResourceType.PartitionKeyRange || - resourceType == ResourceType.Snapshot || - resourceType == ResourceType.ClientEncryptionKey || - (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) - { - return this.GatewayStoreModel; - } - - if (this.isThinClientEnabled - && operationType == OperationType.Read - && resourceType == ResourceType.Database) - { - return this.GatewayStoreModel; - } - - if (operationType == OperationType.Create - || operationType == OperationType.Upsert) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection || - resourceType == ResourceType.Permission) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Delete) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Read) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else - { - return this.StoreModel; - } - } - - /// - /// The preferred link used in replace operation in SDK. +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Net.Security; + using System.Security; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Common; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Query; + using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Tracing.TraceData; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Client; + using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.FaultInjection; + using Microsoft.Azure.Documents.Routing; + using Newtonsoft.Json; + using ResourceType = Documents.ResourceType; + + /// + /// Provides a client-side logical representation for the Azure Cosmos DB service. + /// This client is used to configure and execute requests against the service. + /// + /// + /// This type is thread safe. + /// + /// + /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. + /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. + /// + /// + /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. + /// Depending on your programming technique, you can do this in one of two ways: + /// + /// + /// + /// 1. By using a language construct such as the using statement in C#. + /// The using statement is actually a syntactic convenience. + /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. + /// + /// + /// + /// + /// + /// + /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. + /// The following example replaces the using block in the previous example with a try/catch/finally block. + /// + /// + /// + /// + /// + /// + internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal + { + private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; + private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; + private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; + private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; + private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; + private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; + private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; + private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; + private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; + private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; + private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; + private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; + private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; + private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; + private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; + // Env variable + private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; + + private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; + private const int DefaultMaxRequestsPerRntbdChannel = 30; + private const int DefaultRntbdPartitionCount = 1; + private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; + private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; + private const int DefaultRntbdPortPoolReuseThreshold = 256; + private const int DefaultRntbdPortPoolBindAttempts = 5; + private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; + private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; + private const bool DefaultEnableCpuMonitor = true; + private const string DefaultInitTaskKey = "InitTaskKey"; + + /// + /// Default thresholds for PPAF request hedging. /// - private string GetLinkForRouting(Documents.Resource resource) - { - // we currently prefer the selflink - return resource.SelfLink ?? resource.AltLink; - } - - internal void EnsureValidOverwrite( - Documents.ConsistencyLevel desiredConsistencyLevel, - OperationType? operationType = null, - ResourceType? resourceType = null) - { - Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; - if (!this.IsValidConsistency( - defaultConsistencyLevel, - desiredConsistencyLevel, - operationType, - resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - desiredConsistencyLevel.ToString(), - defaultConsistencyLevel.ToString())); - } - } - - private bool IsValidConsistency( - Documents.ConsistencyLevel backendConsistency, - Documents.ConsistencyLevel desiredConsistency, - OperationType? operationType, - ResourceType? resourceType) - { - if (this.allowOverrideStrongerConsistency) - { - return true; - } - - return ValidationHelpers.IsValidConsistencyLevelOverwrite( - backendConsistency: backendConsistency, - desiredConsistency: desiredConsistency, - isLocalQuorumConsistency: this.IsLocalQuorumConsistency, - operationType: operationType, - resourceType: resourceType); - } - - private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) - { - // Check if we have a store client factory in input and if we do, do not initialize another store client - // The purpose is to reuse store client factory across all document clients inside compute gateway - if (storeClientFactory != null) - { - this.storeClientFactory = storeClientFactory; - this.isStoreClientFactoryCreatedInternally = false; - } - else - { - // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, - // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. - Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () - { -#if INTERNAL - IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing -#else - IsDistributedTracingEnabled = false + private const int DefaultHedgingThresholdInMilliseconds = 1000; + private const int DefaultHedgingThresholdStepInMilliseconds = 500; + + private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; + private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; + + private readonly bool IsLocalQuorumConsistency = false; + private readonly bool isReplicaAddressValidationEnabled; + private readonly bool enableAsyncCacheExceptionNoSharing; + + private readonly bool isThinClientEnabled; + + //Fault Injection + private readonly IChaosInterceptorFactory chaosInterceptorFactory; + private readonly IChaosInterceptor chaosInterceptor; + + private bool isChaosInterceptorInititalized = false; + + //Auth + internal readonly AuthorizationTokenProvider cosmosAuthorization; + + // Gateway has backoff/retry logic to hide transient errors. + private RetryPolicy retryPolicy; + private bool allowOverrideStrongerConsistency = false; + private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; + private int openConnectionTimeoutInSeconds = 5; + private int idleConnectionTimeoutInSeconds = -1; + private int timerPoolGranularityInSeconds = 1; + private bool enableRntbdChannel = true; + private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; + private int rntbdPartitionCount = DefaultRntbdPartitionCount; + private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; + private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; + private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; + private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + private bool enableCpuMonitor = DefaultEnableCpuMonitor; + private int rntbdMaxConcurrentOpeningConnectionCount = 5; + private string clientId; + + //Consistency + private Documents.ConsistencyLevel? desiredConsistencyLevel; + + internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } + + internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } + + private ClientCollectionCache collectionCache; + + private PartitionKeyRangeCache partitionKeyRangeCache; + + //Private state. + private bool isSuccessfullyInitialized; + private bool isDisposed; + + // creator of TransportClient is responsible for disposing it. + private IStoreClientFactory storeClientFactory; + internal CosmosHttpClient httpClient { get; private set; } + + // Flag that indicates whether store client factory must be disposed whenever client is disposed. + // Setting this flag to false will result in store client factory not being disposed when client is disposed. + // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. + private bool isStoreClientFactoryCreatedInternally; + + //Id counter. + private static int idCounter; + //Trace Id. + private int traceId; + + //RemoteCertificateValidationCallback + internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; + + //Distributed Tracing Flag + internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; + + //SessionContainer. + internal ISessionContainer sessionContainer; + + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + private AsyncLazy queryPartitionProvider; + + private DocumentClientEventSource eventSource; + private Func> initializeTaskFactory; + internal AsyncCacheNonBlocking initTaskCache; + + private JsonSerializerSettings serializerSettings; + private event EventHandler sendingRequest; + private event EventHandler receivedResponse; + private Func transportClientHandlerFactory; + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + { + if (authKey == null) + { + throw new ArgumentNullException("authKey"); + } + + if (authKey != null) + { + this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); + } + + this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); + this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The connection policy for the client. + /// + /// + /// This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + HttpMessageHandler handler, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) + { + } + + internal DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null) + : this(serviceEndpoint, + AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), + sendingRequestEventArgs, + connectionPolicy, + desiredConsistencyLevel, + serializerSettings, + apitype, + receivedResponseEventArgs, + handler, + sessionContainer, + enableCpuMonitor, + transportClientHandlerFactory, + storeClientFactory) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The cosmos authorization for the client. + /// The event handler to be invoked before the request is sent. + /// The event handler to be invoked after a response has been received. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// Api type for the account + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// The default session container with which DocumentClient is created. + /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. + /// Transport client handler factory. + /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. + /// Flag to allow Quorum Read with Eventual Consistency Account + /// + /// This delegate responsible for validating the third party certificate. + /// This is distributed tracing flag + /// This is the chaos interceptor used for fault injection + /// A boolean flag indicating if stack trace optimization is enabled. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + internal DocumentClient(Uri serviceEndpoint, + AuthorizationTokenProvider cosmosAuthorization, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null, + bool isLocalQuorumConsistency = false, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + IChaosInterceptorFactory chaosInterceptorFactory = null, + bool enableAsyncCacheExceptionNoSharing = true) + { + if (sendingRequestEventArgs != null) + { + this.sendingRequest += sendingRequestEventArgs; + } + + if (serializerSettings != null) + { + this.serializerSettings = serializerSettings; + } + + this.ApiType = apitype; + + if (receivedResponseEventArgs != null) + { + this.receivedResponse += receivedResponseEventArgs; + } + + this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; + this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); + this.transportClientHandlerFactory = transportClientHandlerFactory; + this.IsLocalQuorumConsistency = isLocalQuorumConsistency; + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.chaosInterceptorFactory = chaosInterceptorFactory; + this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); + this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + + this.Initialize( + serviceEndpoint: serviceEndpoint, + connectionPolicy: connectionPolicy, + desiredConsistencyLevel: desiredConsistencyLevel, + handler: handler, + sessionContainer: sessionContainer, + enableCpuMonitor: enableCpuMonitor, + storeClientFactory: storeClientFactory, + cosmosClientId: cosmosClientId, + remoteCertificateValidationCallback: remoteCertificateValidationCallback, + cosmosClientTelemetryOptions: cosmosClientTelemetryOptions, + enableThinClientMode: this.isThinClientEnabled); + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The connection policy for the client. + /// The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Internal constructor purely for unit-testing + /// + internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) + { + // do nothing + this.ServiceEndpoint = serviceEndpoint; + this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); + } + + internal virtual async Task GetCollectionCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.collectionCache; + } + } + + internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.partitionKeyRangeCache; + } + } + + internal GlobalAddressResolver AddressResolver { get; private set; } + + internal GlobalEndpointManager GlobalEndpointManager { get; private set; } + + internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } + + /// + /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. + /// + /// + /// A object. + /// + /// + /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. + /// If there are any initialization exceptions, this method will throw them (set on the task). + /// Alternately, calling any API will throw initialization exception at the first call. + /// + /// + /// + /// + /// + /// + public Task OpenAsync(CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); + } + + private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); + } + + private async Task OpenPrivateAsync(CancellationToken cancellationToken) + { + // Initialize caches for all databases and collections + ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( + new FeedOptions { MaxItemCount = -1 }); + + try + { + while (databaseFeedReader.HasMoreResults) + { + foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) + { + ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( + database.SelfLink, + new FeedOptions { MaxItemCount = -1 }); + List tasks = new List(); + while (collectionFeedReader.HasMoreResults) + { + tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); + } + + await Task.WhenAll(tasks); + } + } + } + catch (DocumentClientException ex) + { + // Clear the caches to ensure that we don't have partial results + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + + DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); + } + } + + internal virtual void Initialize(Uri serviceEndpoint, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + IStoreClientFactory storeClientFactory = null, + TokenCredential tokenCredential = null, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + bool enableThinClientMode = false) + { + if (serviceEndpoint == null) + { + throw new ArgumentNullException("serviceEndpoint"); + } + + this.clientId = cosmosClientId; + this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; + this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); + + this.queryPartitionProvider = new AsyncLazy(async () => + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); + }, CancellationToken.None); + +#if !(NETSTANDARD15 || NETSTANDARD16) +#if NETSTANDARD20 + // GetEntryAssembly returns null when loaded from native netstandard2.0 + if (System.Reflection.Assembly.GetEntryAssembly() != null) + { +#endif + // For tests we want to allow stronger consistency during construction or per call + string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; + if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) + { + if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) + { + this.allowOverrideStrongerConsistency = false; + } + } + + // We might want to override the defaults sometime + string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; + if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) + { + int maxConcurrentConnectionOpenRequestOverrideInt = 0; + if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) + { + this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; + } + } + + string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) + { + int openConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) + { + this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; + } + } + + string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) + { + int idleConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) + { + this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; + } + } + + string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; + if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) + { + int timerPoolGranularityInSecondsOverrideInt = 0; + if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) + { + // timeoutgranularity specified should be greater than min(5 seconds) + if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) + { + this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; + } + } + } + + string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; + if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) + { + bool enableRntbdChannel = false; + if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) + { + this.enableRntbdChannel = enableRntbdChannel; + } + } + + string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; + if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) + { + int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; + if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) + { + this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; + } + } + + string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; + if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) + { + int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; + if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) + { + this.rntbdPartitionCount = rntbdPartitionCount; + } + } + + string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; + if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) + { + int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) + { + this.maxRntbdChannels = maxRntbdChannels; + } + } + + string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; + if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) + { + PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; + if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) + { + this.rntbdPortReuseMode = portReuseMode; + } + } + + string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; + if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) + { + int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; + if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) + { + this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; + } + } + + string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; + if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) + { + int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; + if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) + { + this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; + } + } + + string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) + { + int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) + { + this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; + } + } + + string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) + { + int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) + { + this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; + } + } + + if (enableCpuMonitor.HasValue) + { + this.enableCpuMonitor = enableCpuMonitor.Value; + } + else + { + string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; + if (!string.IsNullOrEmpty(enableCpuMonitorString)) + { + bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; + if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) + { + this.enableCpuMonitor = enableCpuMonitorFlag; + } + } + } +#if NETSTANDARD20 + } +#endif +#endif + + string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); + if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) + { + if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) + { + if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) + { + throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); + } + + this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; + } + } + + // ConnectionPolicy always overrides appconfig + if (connectionPolicy != null) + { + if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) + { + this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) + { + this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) + { + this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; + } + + if (connectionPolicy.MaxTcpPartitionCount.HasValue) + { + this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; + } + + if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) + { + this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; + } + + if (connectionPolicy.PortReuseMode.HasValue) + { + this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; + } + } + + this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); + + this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; + +#if !NETSTANDARD16 + if (ServicePointAccessor.IsSupported) + { + ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); + servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; + } #endif - - }; - - StoreClientFactory newClientFactory = new StoreClientFactory( - this.ConnectionPolicy.ConnectionProtocol, - (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, - this.maxConcurrentConnectionOpenRequests, - this.ConnectionPolicy.UserAgentContainer, - this.eventSource, - null, - this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.timerPoolGranularityInSeconds, - this.maxRntbdChannels, - this.rntbdPartitionCount, - this.maxRequestsPerRntbdChannel, - (Documents.PortReuseMode)this.rntbdPortReuseMode, - this.rntbdPortPoolReuseThreshold, - this.rntbdPortPoolBindAttempts, - receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, - sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, - retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), - enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, - remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, - distributedTracingOptions: distributedTracingOptions, - enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), - chaosInterceptor: this.chaosInterceptor); - - if (this.transportClientHandlerFactory != null) - { - newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); - } - - this.storeClientFactory = newClientFactory; - this.isStoreClientFactoryCreatedInternally = true; + this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); + + this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( + this.ApiType, + DocumentClientEventSource.Instance, + this.ConnectionPolicy, + handler, + this.sendingRequest, + this.receivedResponse, + this.chaosInterceptor); + + // Loading VM Information (non blocking call and initialization won't fail if this call fails) + VmMetadataApiHandler.TryInitialize(this.httpClient); + + if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) + { + CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); + CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); + + CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); + } + + // Starting ClientTelemetry Job + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, + this.ConnectionPolicy, + this.cosmosAuthorization, + this.httpClient, + this.ServiceEndpoint, + this.GlobalEndpointManager, + this.cancellationTokenSource, + this.chaosInterceptor is not null); + + if (sessionContainer != null) + { + this.sessionContainer = sessionContainer; + } + else + { + this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); + } + + this.desiredConsistencyLevel = desiredConsistencyLevel; + // Setup the proxy to be used based on connection mode. + // For gateway: GatewayProxy. + // For direct: WFStoreProxy [set in OpenAsync()]. + this.eventSource = DocumentClientEventSource.Instance; + + this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( + () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), + new ResourceThrottleRetryPolicy( + this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, + this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); + + // Create the task to start the initialize task + // Task will be awaited on in the EnsureValidClientAsync + Task initTask = this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + + // ContinueWith on the initialization task is needed for handling the UnobservedTaskException + // if this task throws for some reason. Awaiting inside a constructor is not supported and + // even if we had to await inside GetInitializationTask to catch the exception, that will + // be a blocking call. In such cases, the recommended approach is to "handle" the + // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted + // and accessing the Exception property on the target task. +#pragma warning disable VSTHRD110 // Observe result of async calls +#pragma warning disable CDX1000 // DontConvertExceptionToObject + initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); +#pragma warning restore CDX1000 // DontConvertExceptionToObject +#pragma warning restore VSTHRD110 // Observe result of async calls + + this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); + DefaultTrace.TraceInformation(string.Format( + CultureInfo.InvariantCulture, + "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", + this.traceId, + serviceEndpoint.ToString(), + this.ConnectionPolicy.ConnectionMode.ToString(), + this.ConnectionPolicy.ConnectionProtocol.ToString(), + desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); + + this.QueryCompatibilityMode = QueryCompatibilityMode.Default; + } + + // Always called from under the lock except when called from Intilialize method during construction. + private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) + { + await this.InitializeGatewayConfigurationReaderAsync(); + + if (this.desiredConsistencyLevel.HasValue) + { + this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); } - this.AddressResolver = new GlobalAddressResolver( - this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.ConnectionPolicy.ConnectionProtocol, - this, - this.collectionCache, - this.partitionKeyRangeCache, - this.accountServiceConfiguration, - this.ConnectionPolicy, - this.httpClient, - this.storeClientFactory.GetConnectionStateListener(), - this.enableAsyncCacheExceptionNoSharing); - - this.CreateStoreModel(subscribeRntbdStatus: true); - } - - private void CreateStoreModel(bool subscribeRntbdStatus) - { - //EnableReadRequestsFallback, if not explicity set on the connection policy, - //is false if the account's consistency is bounded staleness, - //and true otherwise. - StoreClient storeClient = this.storeClientFactory.CreateStoreClient( - this.AddressResolver, - this.sessionContainer, - this.accountServiceConfiguration, - this, - true, - this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), - !this.enableRntbdChannel, - this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), - true, - enableReplicaValidation: this.isReplicaAddressValidationEnabled, - sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); - - if (subscribeRntbdStatus) + if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride + && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) { - storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); + this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; } - storeClient.SerializerSettings = this.serializerSettings; - - this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); - } - - private void DisableRntbdChannel() - { - Debug.Assert(this.enableRntbdChannel); - this.enableRntbdChannel = false; - this.CreateStoreModel(subscribeRntbdStatus: false); - } + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; + this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); + this.InitializePartitionLevelFailoverWithDefaultHedging(); - private async Task InitializeGatewayConfigurationReaderAsync() - { - GatewayAccountReader accountReader = new GatewayAccountReader( - serviceEndpoint: this.ServiceEndpoint, - cosmosAuthorization: this.cosmosAuthorization, - connectionPolicy: this.ConnectionPolicy, - httpClient: this.httpClient, - cancellationToken: this.cancellationTokenSource.Token, - isThinClientEnabled: this.isThinClientEnabled); + this.PartitionKeyRangeLocation = + this.ConnectionPolicy.EnablePartitionLevelFailover + || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker + ? new GlobalPartitionEndpointManagerCore( + this.GlobalEndpointManager, + this.ConnectionPolicy.EnablePartitionLevelFailover, + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) + : GlobalPartitionEndpointManagerNoOp.Instance; - this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); + this.retryPolicy = new RetryPolicy( + globalEndpointManager: this.GlobalEndpointManager, + connectionPolicy: this.ConnectionPolicy, + partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation); + + this.ResetSessionTokenRetryPolicy = this.retryPolicy; + + GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( + this.GlobalEndpointManager, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + this.PartitionKeyRangeLocation, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + + this.GatewayStoreModel = gatewayStoreModel; + + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); + + gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway && this.isThinClientEnabled) + { + ThinClientStoreModel thinClientStoreModel = new ( + endpointManager: this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + this.chaosInterceptor); + + thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + + this.StoreModel = thinClientStoreModel; + } + else if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) + { + this.StoreModel = this.GatewayStoreModel; + } + else + { + this.InitializeDirectConnectivity(storeClientFactory); + } + + return true; + } + + private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) + { + if (databaseName == null) + { + throw new ArgumentNullException(nameof(databaseName)); + } + + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + using ( + DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Query, + ResourceType.Document, + collection.SelfLink, + AuthorizationTokenType.PrimaryMasterKey)) + { + ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( + resolvedCollection.ResourceId, + new Range( + PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, + PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, + true, + false), + NoOpTrace.Singleton); + + // In Gateway mode, AddressCache is null + if (this.AddressResolver != null) + { + await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); + } + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. + /// + /// + /// + /// The session object used for version tracking when the consistency level is set to Session. + /// + /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. + /// + public object Session + { + get + { + return this.sessionContainer; + } + + set + { + SessionContainer container = value as SessionContainer; + if (container == null) + { + throw new ArgumentNullException("value"); + } + + if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + ClientResources.BadSession, + container.HostName, + this.ServiceEndpoint.Host)); + } + + SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; + if (currentSessionContainer == null) + { + throw new ArgumentNullException(nameof(currentSessionContainer)); + } + + currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. + /// + /// Collection for which session token must be retrieved. + /// + /// The session token used for version tracking when the consistency level is set to Session. + /// + /// + /// The session token can be saved and supplied to a request via . + /// + internal string GetSessionToken(string collectionLink) + { + SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; + + if (sessionContainerInternal == null) + { + throw new ArgumentNullException(nameof(sessionContainerInternal)); + } + + return sessionContainerInternal.GetSessionToken(collectionLink); + } + + /// + /// Gets the Api type + /// + internal ApiType ApiType + { + get; private set; + } + + internal bool UseMultipleWriteLocations { get; private set; } + + /// + /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. + /// + /// + /// The Uri for the service endpoint. + /// + /// + public Uri ServiceEndpoint + { + get; + private set; + } + + /// + /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri WriteEndpoint + { + get + { + return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri ReadEndpoint + { + get + { + return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the Connection policy used by the client from the Azure Cosmos DB service. + /// + /// + /// The Connection policy used by the client. + /// + /// + public ConnectionPolicy ConnectionPolicy { get; private set; } + + /// + /// Gets the AuthKey used by the client from the Azure Cosmos DB service. + /// + /// + /// The AuthKey used by the client. + /// + /// + public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); + + /// + /// Gets the configured consistency level of the client from the Azure Cosmos DB service. + /// + /// + /// The configured of the client. + /// + /// + public virtual Documents.ConsistencyLevel ConsistencyLevel + { + get + { +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : + this.accountServiceConfiguration.DefaultConsistencyLevel; + } + } + + /// + /// Returns the account properties available in the service configuration if the client was initialized. + /// + public bool TryGetCachedAccountProperties(out AccountProperties properties) + { + if (this.isSuccessfullyInitialized + && this.accountServiceConfiguration != null + && this.accountServiceConfiguration.AccountProperties != null) + { + properties = this.accountServiceConfiguration.AccountProperties; + return true; + } + + properties = null; + return false; + } + + /// + /// Disposes the client for the Azure Cosmos DB service. + /// + /// + /// + /// + /// + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + if (this.telemetryToServiceHelper != null) + { + this.telemetryToServiceHelper.Dispose(); + this.telemetryToServiceHelper = null; + } + + if (!this.cancellationTokenSource.IsCancellationRequested) + { + this.cancellationTokenSource.Cancel(); + } + + this.cancellationTokenSource.Dispose(); + + if (this.StoreModel != null) + { + this.StoreModel.Dispose(); + this.StoreModel = null; + } + + if (this.storeClientFactory != null) + { + // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference + if (this.isStoreClientFactoryCreatedInternally) + { + this.storeClientFactory.Dispose(); + } + + this.storeClientFactory = null; + } + + if (this.AddressResolver != null) + { + this.AddressResolver.Dispose(); + this.AddressResolver = null; + } + + if (this.httpClient != null) + { + try + { + this.httpClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.httpClient = null; + } + + if (this.cosmosAuthorization != null) + { + this.cosmosAuthorization.Dispose(); + } + + if (this.GlobalEndpointManager != null) + { + this.GlobalEndpointManager.Dispose(); + this.GlobalEndpointManager = null; + } + + if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) + { + this.queryPartitionProvider.Value.Dispose(); + } + + if (this.initTaskCache != null) + { + this.initTaskCache.Dispose(); + this.initTaskCache = null; + } + + DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); + DefaultTrace.Flush(); + + this.isDisposed = true; + } + + //Compatibility mode: + // Allows to specify compatibility mode used by client when making query requests. + // should be removed when application/sql is no longer supported. + internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } + + /// + /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). + /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache + /// to resolves the request's collection name. If it differs from the session container's resource id it + /// explains the session unavailable exception: somebody removed and recreated the collection. In this + /// case we retry once again (with empty session token) otherwise we return the error to the client + /// (see RenameCollectionAwareClientRetryPolicy) + /// + internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } + + /// + /// Gets and sets the IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension StoreModel { get; set; } + + /// + /// Gets and sets the gateway IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension GatewayStoreModel { get; set; } + + /// + /// Gets and sets on execute scalar query callback + /// + /// + /// Test hook to enable unit test for scalar queries + /// + internal Action OnExecuteScalarQueryCallback { get; set; } + + internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; + + internal virtual async Task GetDefaultConsistencyLevelAsync() + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; + } + + internal Task GetDesiredConsistencyLevelAsync() + { + return Task.FromResult(this.desiredConsistencyLevel); + } + + internal async Task ProcessRequestAsync( + string verb, + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + string testAuthorization = null) // Only for unit-tests + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (verb == null) + { + throw new ArgumentNullException(nameof(verb)); + } + + (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( + request.ResourceAddress, + PathsHelper.GetResourcePath(request.ResourceType), + verb, + request.Headers, + AuthorizationTokenType.PrimaryMasterKey); + + // Unit-test hook + if (testAuthorization != null) + { + payload = testAuthorization; + authorization = testAuthorization; + } + request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; + + try + { + return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); + } + catch (DocumentClientException dce) + { + this.cosmosAuthorization.TraceUnauthorized( + dce, + authorization, + payload); + + throw; + } + } + + internal Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); + } + + internal async Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + ITrace trace, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(trace); + + retryPolicyInstance?.OnBeforeSendRequest(request); + + using (new ActivityScope(Guid.NewGuid())) + { + IStoreModel storeProxy = this.GetStoreProxy(request); + return await storeProxy.ProcessMessageAsync(request, cancellationToken); + } + } + + /// + /// Establishes and Initializes the Rntbd connection to all the backend replica nodes + /// for the given database name and container. + /// + /// A string containing the cosmos database name. + /// A string containing the cosmos container link uri. + /// An instance of the . + internal async Task OpenConnectionsToAllReplicasAsync( + string databaseName, + string containerLinkUri, + CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(databaseName) || + string.IsNullOrEmpty(containerLinkUri)) + { + string resourceName = string.IsNullOrEmpty(databaseName) ? + nameof(databaseName) : + nameof(containerLinkUri); + + throw new ArgumentNullException(resourceName); + } + + if (this.StoreModel != null) + { + try + { + await this.StoreModel.OpenConnectionsToAllReplicasAsync( + databaseName, + containerLinkUri, + cancellationToken); + } + catch (Exception) + { + throw; + } + } + } + + private static string NormalizeAuthorizationPayload(string input) + { + const int expansionBuffer = 12; + StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); + for (int i = 0; i < input.Length; i++) + { + switch (input[i]) + { + case '\n': + builder.Append("\\n"); + break; + case '/': + builder.Append("\\/"); + break; + default: + builder.Append(input[i]); + break; + } + } + + return builder.ToString(); + } + + internal async Task InitilizeFaultInjectionAsync() + { + if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) + { + this.isChaosInterceptorInititalized = true; + await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); + } + } + + internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) + { + return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.maxRequestsPerRntbdChannel, + this.maxRntbdChannels, + this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + this.rntbdPortReuseMode); + } + + internal virtual async Task EnsureValidClientAsync(ITrace trace) + { + if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) + { + return; + } + + // Trace when the Initialization of client has not been completed. Usually during first call + using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) + { + // If the initialization task failed, we should retry initialization. + // We may end up throwing the same exception but this will ensure that we dont have a + // client which is unusable and can resume working if it failed initialization once. + // If we have to reinitialize the client, it needs to happen in thread safe manner so that + // we dont re-initalize the task again for each incoming call. + try + { + this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + } + catch (DocumentClientException ex) + { + throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( + dce: ex, + trace: trace); + } + catch (Exception e) + { + DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); + childTrace.AddDatum("initializeTask failed", e.Message); + throw; + } + + await this.InitilizeFaultInjectionAsync(); + } + } + + #region Create Impl + /// + /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed. + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); + } + + private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, + Documents.Client.RequestOptions options) + { + if (database == null) + { + throw new ArgumentNullException("database"); + } + + // Doing a Read before Create will give us better latency for existing databases + try + { + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDatabaseAsync(database, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + + /// + /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to create. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// Finally, a Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Create a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, + object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, + CancellationToken cancellationToken = default) + { + // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy); + } + + private async Task> CreateDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 collection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDocumentCollectionPrivateAsync( + string databaseLink, + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse( + await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 DocumentCollection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); + } + + private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( + string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) + { + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make + // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. + Documents.Database database = await this.ReadDatabaseAsync(databaseLink); + + // Doing a Read before Create will give us better latency for existing collections. + // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, + // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. + try + { + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + + /// + /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link to the source object. + /// The target object. + /// (optional)The point in time to restore. If null, use the latest restorable time. + /// (Optional) The for the request. + /// The task object representing the service response for the asynchronous operation. + internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + if (targetDocumentCollection == null) + { + throw new ArgumentNullException("targetDocumentCollection"); + } + + bool isFeed; + string resourceTypeString; + string resourceIdOrFullName; + bool isNameBased; + + string dbsId; + string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); + if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + dbsId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + string sourceCollId; + if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + sourceCollId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + this.ValidateResource(targetDocumentCollection); + + if (options == null) + { + options = new Documents.Client.RequestOptions(); + } + if (!options.RemoteStorageType.HasValue) + { + options.RemoteStorageType = RemoteStorageType.Standard; + } + options.SourceDatabaseId = dbsId; + options.SourceCollectionId = sourceCollId; + if (restoreTime.HasValue) + { + options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + targetDocumentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Get the status of a collection being restored in the Azure Cosmos DB service. + /// + /// The link of the document collection being restored. + /// The task object representing the service response for the asynchronous operation. + internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (string.IsNullOrEmpty(targetDocumentCollectionLink)) + { + throw new ArgumentNullException("targetDocumentCollectionLink"); + } + + ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( + targetDocumentCollectionLink, + new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, + retryPolicyInstance); + string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); + if (restoreState == null) + { + restoreState = RestoreState.RestoreCompleted.ToString(); + } + + DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() + { + State = restoreState + }; + + return ret; + } + + /// + /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ + /// The object to create. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the created object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. + /// + /// + /// 409 + /// + /// Conflict - This means a with an id matching the id field of already existed, + /// or there is already a pending snapshot for the specified resource link. + /// + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that + /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (snapshot == null) + { + throw new ArgumentNullException("snapshot"); + } + + this.ValidateResource(snapshot); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Snapshots_Root, + snapshot, + ResourceType.Snapshot, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Delete Impl + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. snapshots/snapshot_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Replace Impl + /// + /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. + /// + /// the updated document collection. + /// the request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceDocumentCollectionPrivateAsync( + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(documentCollection), + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + // set the session token + if (collection.Resource != null) + { + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + } + return collection; + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If either or is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now dynamically cast doc back to your MyPoco + /// MyPoco poco = (dynamic)doc; + /// + /// //Update some properties of the poco object + /// poco.MyProperty = "updated value"; + /// + /// //Now persist these changes to the database using doc.SelLink and the update poco object + /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if ((options == null) || (options.PartitionKey == null)) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible( + () => this.ReplaceDocumentPrivateAsync( + documentLink, + document, + options, + requestRetryPolicy, + cancellationToken), + requestRetryPolicy, + cancellationToken); + } + + private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// doc.SetPropertyValue("MyProperty", "updated value"); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Document updated = await client.ReplaceDocumentAsync(doc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( + this.GetLinkForRouting(document), + document, + options, + retryPolicyInstance, + cancellationToken), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + this.ValidateResource(document); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + documentLink, + document, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, document, options); + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "sproc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// sproc.Body = "function () {new javascript body for sproc}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceStoredProcedurePrivateAsync( + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(storedProcedure), + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "trigger id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// trigger.Body = "function () {new javascript body for trigger}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// Trigger updated = await client.ReplaceTriggerAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(trigger), + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "udf id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// udf.Body = "function () {new javascript body for udf}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedFunctionPrivateAsync( + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(function), + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. + /// + /// + /// + /// + /// + /// r.ResourceLink == "collection selfLink") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Create a new offer with the changed throughput + /// OfferV2 newOffer = new OfferV2(offer, 5000); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Offer updated = await client.ReplaceOfferAsync(newOffer); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceOfferAsync(Offer offer) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (offer == null) + { + throw new ArgumentNullException("offer"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + offer.SelfLink, + offer, + ResourceType.Offer, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse( + await this.UpdateAsync(request, retryPolicyInstance), + OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "user defined type id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(userDefinedType), + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Read Impl + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Database resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// (docLink); + /// ]]> + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the DocumentCollection to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionPrivateAsync( + string documentCollectionLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the stored procedure to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Trigger to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the User Defined Function to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Conflict to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads an from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Offer to be read. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. + /// + /// + /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about + /// minimum throughput of a Cosmos container (or a database) + /// To retrieve the minimum throughput for a collection/database, use the following sample + /// + /// response = await client.ReadOfferAsync(offer.SelfLink); + /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; + /// ]]> + /// + /// + /// + /// + /// + /// + /// + /// + public Task> ReadOfferAsync(string offerLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(offerLink)) + { + throw new ArgumentNullException("offerLink"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Offer, + offerLink, + null, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Reads a as an asynchronous operation. + /// + /// The link for the schema to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Schema are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentSchemaLink)) + { + throw new ArgumentNullException("documentSchemaLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Schema, + documentSchemaLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the UserDefinedType resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypeLink)) + { + throw new ArgumentNullException("userDefinedTypeLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedType, + userDefinedTypeLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Snapshot resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Snapshot are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/snapshots/{snapshot identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region ReadFeed Impl + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDatabaseFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = null; + /// List ids = new List(); + /// do + /// { + /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); + /// foreach (var item in response) + /// { + /// ids.Add(item.Id); + /// } + /// } + /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(partitionKeyRangesLink)) + { + throw new ArgumentNullException("partitionKeyRangesLink"); + } + + return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionsLink)) + { + throw new ArgumentNullException("collectionsLink"); + } + + return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProceduresLink)) + { + throw new ArgumentNullException("storedProceduresLink"); + } + + return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggersLink)) + { + throw new ArgumentNullException("triggersLink"); + } + + return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedFunctionsLink)) + { + throw new ArgumentNullException("userDefinedFunctionsLink"); + } + + return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. + /// This takes returns a which will contain an enumerable list of dynamic objects. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a containing dynamic objects representing the items in the feed. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. + /// This is important becuse a DocumentCollection can contain different kinds of documents. + /// + /// + /// + /// + public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentsLink)) + { + throw new ArgumentNullException("documentsLink"); + } + + DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); + return new DocumentFeedResponse( + response.Cast(), + response.Count, + response.Headers, + response.UseETagAsContinuation, + response.QueryMetrics, + response.RequestStatistics, + responseLengthBytes: response.ResponseLengthBytes); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); + } + + private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictsLink)) + { + throw new ArgumentNullException("conflictsLink"); + } + + return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service + /// as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadOfferAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadOffersFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionSchemaLink)) + { + throw new ArgumentNullException("documentCollectionSchemaLink"); + } + + return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypesLink)) + { + throw new ArgumentNullException("userDefinedTypesLink"); + } + + return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a set of containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadSnapshotFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); + } + + #endregion + + #region Stored procs + /// + /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) + { + return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + default, + procedureParams), + retryPolicyInstance); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + cancellationToken, + procedureParams), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ExecuteStoredProcedurePrivateAsync( + string storedProcedureLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + params dynamic[] procedureParams) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); + string storedProcedureInput = serializerSettings == null ? + JsonConvert.SerializeObject(procedureParams) : + JsonConvert.SerializeObject(procedureParams, serializerSettings); + using (MemoryStream storedProcedureInputStream = new MemoryStream()) + { + using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) + { + await writer.WriteAsync(storedProcedureInput); + await writer.FlushAsync(); + storedProcedureInputStream.Position = 0; + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.ExecuteJavaScript, + ResourceType.StoredProcedure, + storedProcedureLink, + storedProcedureInputStream, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); + if (options?.PartitionKeyRangeId == null) + { + await this.AddPartitionKeyInformationAsync( + request, + options); + } + + retryPolicyInstance?.OnBeforeSendRequest(request); + + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new StoredProcedureResponse(await this.ExecuteProcedureAsync( + request, + retryPolicyInstance, + cancellationToken), + this.GetSerializerSettingsForRequest(options)); + } + } + } + } + + #endregion + + #region Upsert Impl + /// + /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to upsert. + /// (Optional) The for the request. + /// The that was upserted within a task object representing the service response for the asynchronous operation. + /// If is not set + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// + /// + /// The example below upserts a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to upsert. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// A Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Upsert a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) + { + // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy, cancellationToken); + } + + private async Task> UpsertDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + // To be implemented. + throw new NotImplementedException(); + } + + /// + /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. + /// + /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ + /// The object to upsert. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the upserted object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + #endregion + + #region IAuthorizationTokenProvider + + ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType) + { + return this.cosmosAuthorization.GetUserAuthorizationAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType); + } + + ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType, + ITrace trace) + { + return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType, + trace); + } + + Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( + DocumentServiceRequest request, + string federationId, + string verb, + string resourceId) + { + return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( + request, + federationId, + verb, + resourceId); + } + + #endregion + + #region Core Implementation + internal Task CreateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpdateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); + } + + internal Task ReadAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task ReadFeedAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task DeleteAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteProcedureAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteQueryAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpsertAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + #endregion + + /// + /// Read the from the Azure Cosmos DB service as an asynchronous operation. + /// + /// + /// A wrapped in a object. + /// + public Task GetDatabaseAccountAsync() + { + return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); + } + + /// + /// Read the as an asynchronous operation + /// given a specific reginal endpoint url. + /// + /// The reginal url of the serice endpoint. + /// The CancellationToken + /// + /// A wrapped in a object. + /// + Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) + { + return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); + } + + private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) + { + async ValueTask CreateRequestMessage() + { + HttpRequestMessage request = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = serviceEndpoint + }; + + INameValueCollection headersCollection = new StoreResponseNameValueCollection(); + await this.cosmosAuthorization.AddAuthorizationHeaderAsync( + headersCollection, + serviceEndpoint, + "GET", + AuthorizationTokenType.PrimaryMasterKey); + + foreach (string key in headersCollection.AllKeys()) + { + request.Headers.Add(key, headersCollection[key]); + } + + return request; + } + + AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, + clientSideRequestStatistics: null); + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; + + if (this.queryPartitionProvider.IsValueCreated) + { + (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); + } + + return databaseAccount; + } + + return null; + } + + #region Private Impl + + /// + /// Certain requests must be routed through gateway even when the client connectivity mode is direct. + /// For e.g., DocumentCollection creation. This method returns the based + /// on the input . + /// + /// Returns to which the request must be sent + internal IStoreModel GetStoreProxy(DocumentServiceRequest request) + { + // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) + // we return the Gateway store model + if (request.UseGatewayMode) + { + return this.GatewayStoreModel; + } + + ResourceType resourceType = request.ResourceType; + OperationType operationType = request.OperationType; + + if (resourceType == ResourceType.Offer || + (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || + resourceType == ResourceType.PartitionKeyRange || + resourceType == ResourceType.Snapshot || + resourceType == ResourceType.ClientEncryptionKey || + (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) + { + return this.GatewayStoreModel; + } + + if (this.isThinClientEnabled + && operationType == OperationType.Read + && resourceType == ResourceType.Database) + { + return this.GatewayStoreModel; + } + + if (operationType == OperationType.Create + || operationType == OperationType.Upsert) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection || + resourceType == ResourceType.Permission) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Delete) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Read) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else + { + return this.StoreModel; + } + } + + /// + /// The preferred link used in replace operation in SDK. + /// + private string GetLinkForRouting(Documents.Resource resource) + { + // we currently prefer the selflink + return resource.SelfLink ?? resource.AltLink; + } + + internal void EnsureValidOverwrite( + Documents.ConsistencyLevel desiredConsistencyLevel, + OperationType? operationType = null, + ResourceType? resourceType = null) + { + Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; + if (!this.IsValidConsistency( + defaultConsistencyLevel, + desiredConsistencyLevel, + operationType, + resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + desiredConsistencyLevel.ToString(), + defaultConsistencyLevel.ToString())); + } + } + + private bool IsValidConsistency( + Documents.ConsistencyLevel backendConsistency, + Documents.ConsistencyLevel desiredConsistency, + OperationType? operationType, + ResourceType? resourceType) + { + if (this.allowOverrideStrongerConsistency) + { + return true; + } + + return ValidationHelpers.IsValidConsistencyLevelOverwrite( + backendConsistency: backendConsistency, + desiredConsistency: desiredConsistency, + isLocalQuorumConsistency: this.IsLocalQuorumConsistency, + operationType: operationType, + resourceType: resourceType); + } + + private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) + { + // Check if we have a store client factory in input and if we do, do not initialize another store client + // The purpose is to reuse store client factory across all document clients inside compute gateway + if (storeClientFactory != null) + { + this.storeClientFactory = storeClientFactory; + this.isStoreClientFactoryCreatedInternally = false; + } + else + { + // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, + // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. + Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () + { +#if INTERNAL + IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing +#else + IsDistributedTracingEnabled = false +#endif + + }; + + StoreClientFactory newClientFactory = new StoreClientFactory( + this.ConnectionPolicy.ConnectionProtocol, + (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, + this.maxConcurrentConnectionOpenRequests, + this.ConnectionPolicy.UserAgentContainer, + this.eventSource, + null, + this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.timerPoolGranularityInSeconds, + this.maxRntbdChannels, + this.rntbdPartitionCount, + this.maxRequestsPerRntbdChannel, + (Documents.PortReuseMode)this.rntbdPortReuseMode, + this.rntbdPortPoolReuseThreshold, + this.rntbdPortPoolBindAttempts, + receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, + sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, + retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), + enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, + remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, + distributedTracingOptions: distributedTracingOptions, + enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), + chaosInterceptor: this.chaosInterceptor); + + if (this.transportClientHandlerFactory != null) + { + newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); + } + + this.storeClientFactory = newClientFactory; + this.isStoreClientFactoryCreatedInternally = true; + } + + this.AddressResolver = new GlobalAddressResolver( + this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.ConnectionPolicy.ConnectionProtocol, + this, + this.collectionCache, + this.partitionKeyRangeCache, + this.accountServiceConfiguration, + this.ConnectionPolicy, + this.httpClient, + this.storeClientFactory.GetConnectionStateListener(), + this.enableAsyncCacheExceptionNoSharing); + + this.CreateStoreModel(subscribeRntbdStatus: true); + } + + private void CreateStoreModel(bool subscribeRntbdStatus) + { + //EnableReadRequestsFallback, if not explicity set on the connection policy, + //is false if the account's consistency is bounded staleness, + //and true otherwise. + StoreClient storeClient = this.storeClientFactory.CreateStoreClient( + this.AddressResolver, + this.sessionContainer, + this.accountServiceConfiguration, + this, + true, + this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), + !this.enableRntbdChannel, + this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), + true, + enableReplicaValidation: this.isReplicaAddressValidationEnabled, + sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); + + if (subscribeRntbdStatus) + { + storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); + } + + storeClient.SerializerSettings = this.serializerSettings; + + this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); + } + + private void DisableRntbdChannel() + { + Debug.Assert(this.enableRntbdChannel); + this.enableRntbdChannel = false; + this.CreateStoreModel(subscribeRntbdStatus: false); + } + + private async Task InitializeGatewayConfigurationReaderAsync() + { + GatewayAccountReader accountReader = new GatewayAccountReader( + serviceEndpoint: this.ServiceEndpoint, + cosmosAuthorization: this.cosmosAuthorization, + connectionPolicy: this.ConnectionPolicy, + httpClient: this.httpClient, + cancellationToken: this.cancellationTokenSource.Token, + isThinClientEnabled: this.isThinClientEnabled); + + this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); + await this.accountServiceConfiguration.InitializeAsync(); AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; - this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); - } + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; + this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); + } internal string GetUserAgentFeatures() { @@ -6888,379 +6887,379 @@ internal void InitializePartitionLevelFailoverWithDefaultHedging() thresholdStep: TimeSpan.FromMilliseconds(DocumentClient.DefaultHedgingThresholdStepInMilliseconds)); } } - - internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) - { - this.sessionContainer.SetSessionToken(request, response.Headers); - } - - internal DocumentServiceRequest CreateDocumentServiceRequest( - OperationType operationType, - string resourceLink, - ResourceType resourceType, - INameValueCollection headers) - { - if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) - { - return DocumentServiceRequest.Create( - operationType, - null, - resourceType, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - else - { - return DocumentServiceRequest.Create( - operationType, - resourceType, - resourceLink, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - } - - internal void ValidateResource(Documents.Resource resource) - { - this.ValidateResource(resource.Id); - } - - internal void ValidateResource(string resourceId) - { - if (!string.IsNullOrEmpty(resourceId)) - { - int match = resourceId.IndexOfAny(resourceIdSeparators); - if (match != -1) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidCharacterInResourceName, - resourceId[match])); - } - - if (resourceId[resourceId.Length - 1] == ' ') - { - throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); - } - } - } - - private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - PartitionKeyInternal partitionKey; - if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else if (options?.PartitionKey != null) - { - partitionKey = options.PartitionKey.InternalKey; - } - else - { - partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - // For backward compatibility, if collection doesn't have partition key defined, we assume all documents - // have empty value for it and user doesn't need to specify it explicitly. - PartitionKeyInternal partitionKey; - if (options?.PartitionKey == null) - { - if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) - { - partitionKey = PartitionKeyInternal.Empty; - } - else - { - throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); - } - } - else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else - { - partitionKey = options.PartitionKey.InternalKey; - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) - { - return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; - } - - private INameValueCollection GetRequestHeaders( - Documents.Client.RequestOptions options, - OperationType operationType, - ResourceType resourceType) - { - Debug.Assert( - this.isSuccessfullyInitialized, - "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); - - RequestNameValueCollection headers = new RequestNameValueCollection(); - - if (this.UseMultipleWriteLocations) - { - headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); - } - - if (this.desiredConsistencyLevel.HasValue) - { - // check anyways since default consistency level might have been refreshed. - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: this.desiredConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); - } - - if (options == null) - { - return headers; - } - - if (options.AccessCondition != null) - { - if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) - { - headers.IfMatch = options.AccessCondition.Condition; - } - else - { - headers.IfNoneMatch = options.AccessCondition.Condition; - } - } - - if (options.ConsistencyLevel.HasValue) - { - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: options.ConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); - } - - if (options.PriorityLevel.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); - } - - if (options.IndexingDirective.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); - } - - if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) - { - string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); - } - - if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) - { - string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); - } - - if (!string.IsNullOrEmpty(options.SessionToken)) - { - headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; - } - - if (options.ResourceTokenExpirySeconds.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferType != null) - { - headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); - } - - if (options.OfferThroughput.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferEnableRUPerMinuteThroughput) - { - headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); - } - - if (options.InsertSystemPartitionKey) - { - headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); - } - - //if (options.OfferAutopilotTier.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); - //} - - //if (options.OfferAutopilotAutoUpgrade.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); - //} - - if (options.EnableScriptLogging) - { - headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); - } - - if (options.PopulateQuotaInfo) - { - headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); - } - - if (options.PopulateRestoreStatus) - { - headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); - } - - if (options.PopulatePartitionKeyRangeStatistics) - { - headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); - } - - if (options.DisableRUPerMinuteUsage) - { - headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); - } - - if (options.RemoteStorageType.HasValue) - { - headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); - } - - if (options.PartitionKeyRangeId != null) - { - headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); - } - - if (options.SourceDatabaseId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); - } - - if (options.SourceCollectionId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); - } - - if (options.RestorePointInTime.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.IsReadOnlyScript) - { - headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); - } - - if (options.IncludeSnapshotDirectories) - { - headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); - } - - if (options.ExcludeSystemProperties.HasValue) - { - headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); - } - - if (options.MergeStaticId != null) - { - headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); - } - - if (options.PreserveFullContent) - { - headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); - } - - if (options.ThroughputBucket.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); - } - - return headers; - } - - private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory - { - private readonly IRetryPolicyFactory retryPolicy; - private readonly ISessionContainer sessionContainer; - private readonly ClientCollectionCache collectionCache; - - public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) - { - this.retryPolicy = retryPolicy; - this.sessionContainer = sessionContainer; - this.collectionCache = collectionCache; - } - - public IDocumentClientRetryPolicy GetRequestPolicy() - { - return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); - } - } - - private class HttpRequestMessageHandler : DelegatingHandler - { - private readonly EventHandler sendingRequest; - private readonly EventHandler receivedResponse; - - public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) - { - this.sendingRequest = sendingRequest; - this.receivedResponse = receivedResponse; - - this.InnerHandler = innerHandler ?? new HttpClientHandler(); - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); - HttpResponseMessage response = await base.SendAsync(request, cancellationToken); - this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); - return response; - } - } - - #endregion - } -} + + internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) + { + this.sessionContainer.SetSessionToken(request, response.Headers); + } + + internal DocumentServiceRequest CreateDocumentServiceRequest( + OperationType operationType, + string resourceLink, + ResourceType resourceType, + INameValueCollection headers) + { + if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) + { + return DocumentServiceRequest.Create( + operationType, + null, + resourceType, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + else + { + return DocumentServiceRequest.Create( + operationType, + resourceType, + resourceLink, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + } + + internal void ValidateResource(Documents.Resource resource) + { + this.ValidateResource(resource.Id); + } + + internal void ValidateResource(string resourceId) + { + if (!string.IsNullOrEmpty(resourceId)) + { + int match = resourceId.IndexOfAny(resourceIdSeparators); + if (match != -1) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidCharacterInResourceName, + resourceId[match])); + } + + if (resourceId[resourceId.Length - 1] == ' ') + { + throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); + } + } + } + + private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + PartitionKeyInternal partitionKey; + if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else if (options?.PartitionKey != null) + { + partitionKey = options.PartitionKey.InternalKey; + } + else + { + partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + // For backward compatibility, if collection doesn't have partition key defined, we assume all documents + // have empty value for it and user doesn't need to specify it explicitly. + PartitionKeyInternal partitionKey; + if (options?.PartitionKey == null) + { + if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) + { + partitionKey = PartitionKeyInternal.Empty; + } + else + { + throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); + } + } + else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else + { + partitionKey = options.PartitionKey.InternalKey; + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) + { + return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; + } + + private INameValueCollection GetRequestHeaders( + Documents.Client.RequestOptions options, + OperationType operationType, + ResourceType resourceType) + { + Debug.Assert( + this.isSuccessfullyInitialized, + "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); + + RequestNameValueCollection headers = new RequestNameValueCollection(); + + if (this.UseMultipleWriteLocations) + { + headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); + } + + if (this.desiredConsistencyLevel.HasValue) + { + // check anyways since default consistency level might have been refreshed. + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: this.desiredConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); + } + + if (options == null) + { + return headers; + } + + if (options.AccessCondition != null) + { + if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) + { + headers.IfMatch = options.AccessCondition.Condition; + } + else + { + headers.IfNoneMatch = options.AccessCondition.Condition; + } + } + + if (options.ConsistencyLevel.HasValue) + { + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: options.ConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); + } + + if (options.PriorityLevel.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); + } + + if (options.IndexingDirective.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); + } + + if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) + { + string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); + } + + if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) + { + string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); + } + + if (!string.IsNullOrEmpty(options.SessionToken)) + { + headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; + } + + if (options.ResourceTokenExpirySeconds.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferType != null) + { + headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); + } + + if (options.OfferThroughput.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferEnableRUPerMinuteThroughput) + { + headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); + } + + if (options.InsertSystemPartitionKey) + { + headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); + } + + //if (options.OfferAutopilotTier.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); + //} + + //if (options.OfferAutopilotAutoUpgrade.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); + //} + + if (options.EnableScriptLogging) + { + headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); + } + + if (options.PopulateQuotaInfo) + { + headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); + } + + if (options.PopulateRestoreStatus) + { + headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); + } + + if (options.PopulatePartitionKeyRangeStatistics) + { + headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); + } + + if (options.DisableRUPerMinuteUsage) + { + headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); + } + + if (options.RemoteStorageType.HasValue) + { + headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); + } + + if (options.PartitionKeyRangeId != null) + { + headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); + } + + if (options.SourceDatabaseId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); + } + + if (options.SourceCollectionId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); + } + + if (options.RestorePointInTime.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.IsReadOnlyScript) + { + headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); + } + + if (options.IncludeSnapshotDirectories) + { + headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); + } + + if (options.ExcludeSystemProperties.HasValue) + { + headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); + } + + if (options.MergeStaticId != null) + { + headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); + } + + if (options.PreserveFullContent) + { + headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); + } + + if (options.ThroughputBucket.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); + } + + return headers; + } + + private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory + { + private readonly IRetryPolicyFactory retryPolicy; + private readonly ISessionContainer sessionContainer; + private readonly ClientCollectionCache collectionCache; + + public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) + { + this.retryPolicy = retryPolicy; + this.sessionContainer = sessionContainer; + this.collectionCache = collectionCache; + } + + public IDocumentClientRetryPolicy GetRequestPolicy() + { + return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); + } + } + + private class HttpRequestMessageHandler : DelegatingHandler + { + private readonly EventHandler sendingRequest; + private readonly EventHandler receivedResponse; + + public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) + { + this.sendingRequest = sendingRequest; + this.receivedResponse = receivedResponse; + + this.InnerHandler = innerHandler ?? new HttpClientHandler(); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); + HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); + return response; + } + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 22a79eefc6..6a0f33131f 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -368,7 +368,7 @@ internal static async Task> TryResolveSessionTokenAsync( return new Tuple(false, null); } - protected static async Task> TryResolvePartitionKeyRangeAsync( + private static async Task> TryResolvePartitionKeyRangeAsync( DocumentServiceRequest request, ISessionContainer sessionContainer, PartitionKeyRangeCache partitionKeyRangeCache, diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs index fc134e2693..4b6c715211 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs @@ -169,9 +169,7 @@ public override bool TryMarkEndpointUnavailableForPartitionKeyRange( { // For multi master write accounts, since all the regions are treated as write regions, the next locations to fail over // will be the preferred read regions that are configured in the application preferred regions in the CosmosClientOptions. - ReadOnlyCollection nextLocations = ConfigurationManager.IsThinClientEnabled(defaultValue: false) && ThinClientStoreModel.IsOperationSupportedByThinClient(request) - ? this.globalEndpointManager.ThinClientReadEndpoints - : this.globalEndpointManager.ReadEndpoints; + ReadOnlyCollection nextLocations = this.globalEndpointManager.ReadEndpoints; return this.TryAddOrUpdatePartitionFailoverInfoAndMoveToNextLocation( partitionKeyRange, @@ -183,9 +181,7 @@ public override bool TryMarkEndpointUnavailableForPartitionKeyRange( else if (this.IsRequestEligibleForPerPartitionAutomaticFailover(request)) { // For any single master write accounts, the next locations to fail over will be the read regions configured at the account level. - ReadOnlyCollection nextLocations = ConfigurationManager.IsThinClientEnabled(defaultValue: false) && ThinClientStoreModel.IsOperationSupportedByThinClient(request) - ? this.globalEndpointManager.ThinClientReadEndpoints - : this.globalEndpointManager.AccountReadEndpoints; + ReadOnlyCollection nextLocations = this.globalEndpointManager.AccountReadEndpoints; return this.TryAddOrUpdatePartitionFailoverInfoAndMoveToNextLocation( partitionKeyRange, diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index 7c444fff83..81b8545f6e 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -1,69 +1,62 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Concurrent; - using System.IO; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Tracing; +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Concurrent; + using System.IO; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.FaultInjection; - using Microsoft.Azure.Documents; - using Newtonsoft.Json; - using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; - - /// - /// A TransportClient that sends requests to proxy endpoint. - /// And then processes the response back into DocumentServiceResponse objects. - /// - internal class ThinClientStoreClient : GatewayStoreClient - { - + using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; + + /// + /// A TransportClient that sends requests to proxy endpoint. + /// And then processes the response back into DocumentServiceResponse objects. + /// + internal class ThinClientStoreClient : GatewayStoreClient + { private readonly ObjectPool bufferProviderWrapperPool; - private readonly IChaosInterceptor chaosInterceptor; - private readonly bool isPartitionLevelFailoverEnabled; - private readonly ObjectPool bufferProviderWrapperPool; - - public ThinClientStoreClient( - CosmosHttpClient httpClient, - ICommunicationEventSource eventSource, + private readonly IChaosInterceptor chaosInterceptor; + + public ThinClientStoreClient( + CosmosHttpClient httpClient, + ICommunicationEventSource eventSource, JsonSerializerSettings serializerSettings = null, - bool isPartitionLevelFailoverEnabled = false, - IChaosInterceptor chaosInterceptor = null) - : base(httpClient, - eventSource, - serializerSettings, - isPartitionLevelFailoverEnabled) - { - this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; - this.chaosInterceptor = chaosInterceptor; - } - - public override async Task InvokeAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceType, - physicalAddress, - thinClientEndpoint, - globalDatabaseAccountName, - clientCollectionCache, - cancellationToken)) + IChaosInterceptor chaosInterceptor = null) + : base(httpClient, + eventSource, + serializerSettings) + { + this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + this.chaosInterceptor = chaosInterceptor; + } + + public override async Task InvokeAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceType, + physicalAddress, + thinClientEndpoint, + globalDatabaseAccountName, + clientCollectionCache, + cancellationToken)) { if (this.chaosInterceptor != null) { @@ -76,43 +69,43 @@ public override async Task InvokeAsync( request.Headers.Remove("FAULTINJECTION_IS_PROXY"); return await ThinClientStoreClient.ParseResponseAsync(fiResponseMessage, request.SerializerSettings ?? base.SerializerSettings, request); } - } - HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); - return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); - } - } - - internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) - { - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? - HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : - HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); - - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceOperation.resourceType, - physicalAddress, - default, - default, - default, - default)) - { - return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); - } - } - - private async ValueTask PrepareRequestForProxyAsync( - DocumentServiceRequest request, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache) - { - HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; - requestMessage.Version = new Version(2, 0); - - BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); - try + } + HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); + return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); + } + } + + internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) + { + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? + HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : + HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); + + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceOperation.resourceType, + physicalAddress, + default, + default, + default, + default)) + { + return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); + } + } + + private async ValueTask PrepareRequestForProxyAsync( + DocumentServiceRequest request, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache) + { + HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; + requestMessage.Version = new Version(2, 0); + + BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); + try { PartitionKeyRange partitionKeyRange = request.RequestContext?.ResolvedPartitionKeyRange; @@ -126,80 +119,80 @@ private async ValueTask PrepareRequestForProxyAsync( ThinClientConstants.ProxyEndEpk, partitionKeyRange?.MaxExclusive); } - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyOperationType, - request.OperationType.ToOperationTypeString()); - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyResourceType, - request.ResourceType.ToResourceTypeString()); - - Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( - bufferProviderWrapper, - globalDatabaseAccountName, - clientCollectionCache, - requestMessage); - - if (!contentStream.CanSeek) - { - throw new InvalidOperationException( - $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); - } - - requestMessage.Content = new StreamContent(contentStream); - requestMessage.Content.Headers.ContentLength = contentStream.Length; + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyOperationType, + request.OperationType.ToOperationTypeString()); + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyResourceType, + request.ResourceType.ToResourceTypeString()); + + Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( + bufferProviderWrapper, + globalDatabaseAccountName, + clientCollectionCache, + requestMessage); + + if (!contentStream.CanSeek) + { + throw new InvalidOperationException( + $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); + } + + requestMessage.Content = new StreamContent(contentStream); + requestMessage.Content.Headers.ContentLength = contentStream.Length; - requestMessage.RequestUri = thinClientEndpoint; - requestMessage.Method = HttpMethod.Post; - - return requestMessage; - } - finally - { - this.bufferProviderWrapperPool.Return(bufferProviderWrapper); - } - } - - private Task InvokeClientAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); - return base.httpClient.SendHttpAsync( - () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), - resourceType, - HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), - request.RequestContext.ClientRequestStatistics, + requestMessage.RequestUri = thinClientEndpoint; + requestMessage.Method = HttpMethod.Post; + + return requestMessage; + } + finally + { + this.bufferProviderWrapperPool.Return(bufferProviderWrapper); + } + } + + private Task InvokeClientAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); + return base.httpClient.SendHttpAsync( + () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), + resourceType, + HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), + request.RequestContext.ClientRequestStatistics, cancellationToken, - request); - } - - internal class ObjectPool - { - private readonly ConcurrentBag Objects; - private readonly Func ObjectGenerator; - - public ObjectPool(Func objectGenerator) - { - this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); - this.Objects = new ConcurrentBag(); - } - - public T Get() - { - return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); - } - - public void Return(T item) - { - this.Objects.Add(item); - } - } - } + request); + } + + internal class ObjectPool + { + private readonly ConcurrentBag Objects; + private readonly Func ObjectGenerator; + + public ObjectPool(Func objectGenerator) + { + this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); + this.Objects = new ConcurrentBag(); + } + + public T Get() + { + return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); + } + + public void Return(T item) + { + this.Objects.Add(item); + } + } + } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs index 020011280d..b077d78e70 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs @@ -20,8 +20,6 @@ namespace Microsoft.Azure.Cosmos /// internal class ThinClientStoreModel : GatewayStoreModel { - private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; - private readonly bool isPartitionLevelFailoverEnabled; private ThinClientStoreClient thinClientStoreClient; public ThinClientStoreModel( @@ -32,7 +30,6 @@ public ThinClientStoreModel( DocumentClientEventSource eventSource, JsonSerializerSettings serializerSettings, CosmosHttpClient httpClient, - bool isPartitionLevelFailoverEnabled = false, IChaosInterceptor chaosInterceptor) : base(endpointManager, sessionContainer, @@ -40,19 +37,13 @@ public ThinClientStoreModel( eventSource, serializerSettings, httpClient, - globalPartitionEndpointManager, - isPartitionLevelFailoverEnabled) + globalPartitionEndpointManager) { this.thinClientStoreClient = new ThinClientStoreClient( httpClient, eventSource, serializerSettings, - isPartitionLevelFailoverEnabled, chaosInterceptor); - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; - this.globalPartitionEndpointManager = globalPartitionEndpointManager; - this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( - base.MarkEndpointsToHealthyAsync); } public override async Task ProcessMessageAsync( @@ -75,6 +66,7 @@ await GatewayStoreModel.ApplySessionTokenAsync( DocumentServiceResponse response; try { + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? base.GetFeedUri(request) : base.GetEntityUri(request); if (request.ResourceType.Equals(ResourceType.Document) && base.endpointManager.TryGetLocationForGatewayDiagnostics( request.RequestContext.LocationEndpointToRoute, out string regionName)) @@ -82,23 +74,6 @@ await GatewayStoreModel.ApplySessionTokenAsync( request.RequestContext.RegionName = regionName; } - // This is applicable for both per partition automatic failover and per partition circuit breaker. - if (this.isPartitionLevelFailoverEnabled - && !ReplicatedResourceClient.IsMasterResource(request.ResourceType) - && request.ResourceType.IsPartitioned()) - { - (bool isSuccess, PartitionKeyRange partitionKeyRange) = await GatewayStoreModel.TryResolvePartitionKeyRangeAsync( - request: request, - sessionContainer: this.sessionContainer, - partitionKeyRangeCache: this.partitionKeyRangeCache, - clientCollectionCache: this.clientCollectionCache, - refreshCache: false); - - request.RequestContext.ResolvedPartitionKeyRange = partitionKeyRange; - this.globalPartitionEndpointManager.TryAddPartitionLevelLocationOverride(request); - } - - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? base.GetFeedUri(request) : base.GetEntityUri(request); AccountProperties properties = await this.GetDatabaseAccountPropertiesAsync(); response = await this.thinClientStoreClient.InvokeAsync( request, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs index eb68823ba0..5d8fee6085 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs @@ -29,9 +29,8 @@ public class CosmosItemIntegrationTests private static string region1; private static string region2; - private static string region3; + private static string region3; private IDictionary readRegionsMapping; - private IList thinClientreadRegionalEndpoints; private CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer; [TestInitialize] @@ -483,8 +482,8 @@ public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountA { // Arrange. Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); - + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + // Enabling fault injection rule to simulate a 503 service unavailable scenario. string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( @@ -595,7 +594,7 @@ public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountA finally { Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); await this.TryDeleteItems(itemsList); } @@ -1736,273 +1735,6 @@ public async Task AddressRefreshInternalServerErrorTest() } } } - - [TestMethod] - [TestCategory("MultiRegion")] - [Ignore("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] - [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task ReadItemAsync_WithThinClientCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( - ConnectionMode connectionMode, - string iterationCount, - string circuitBreakerConsecutiveFailureCount) - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.ReadItem) - .WithRegion(Regions.WestUS) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = connectionMode, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); - - Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); - this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; - - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); - int totalIterations = int.Parse(iterationCount); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - ItemResponse readResponse = await container.ReadItemAsync( - id: itemsList[0].Id, - partitionKey: new PartitionKey(itemsList[0].Pk)); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: readResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - if (attemptCount == consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); - } - } - } - catch (CosmosException ce) - { - Assert.Fail("Read Item operation should succeed." + ce); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); - - await this.TryDeleteItems(itemsList); - } - } - - [TestMethod] - [TestCategory("MultiMaster")] - [Ignore ("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] - [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] - [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] - [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] - [Owner("dkunda")] - [Timeout(70000)] - public async Task CreateItemAsync_WithThinClientEnabledAndCircuitBreakerEnabledAndMultiMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( - ConnectionMode connectionMode, - string iterationCount, - string circuitBreakerConsecutiveFailureCount) - { - // Arrange. - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); - - // Enabling fault injection rule to simulate a 503 service unavailable scenario. - string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); - FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( - id: serviceUnavailableRuleId, - condition: - new FaultInjectionConditionBuilder() - .WithOperationType(FaultInjectionOperationType.CreateItem) - .WithRegion(Regions.WestUS) - .Build(), - result: - FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) - .WithDelay(TimeSpan.FromMilliseconds(10)) - .Build()) - .Build(); - - List rules = new List { serviceUnavailableRule }; - FaultInjector faultInjector = new FaultInjector(rules); - - Random random = new(); - List itemsCleanupList = new(); - List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() - { - ConnectionMode = connectionMode, - ConsistencyLevel = ConsistencyLevel.Session, - FaultInjector = faultInjector, - RequestTimeout = TimeSpan.FromSeconds(5), - ApplicationPreferredRegions = preferredRegions, - }; - - List itemsList = new() - { - new() { Id = "smTestId1", Pk = "smpk1" }, - }; - - try - { - using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); - AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); - - Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); - this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; - - Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); - Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); - - // Act and Assert. - await this.TryCreateItems(itemsList); - - //Must Ensure the data is replicated to all regions - await Task.Delay(3000); - - int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); - int totalIterations = int.Parse(iterationCount); - - for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) - { - try - { - CosmosIntegrationTestObject testItem = new() - { - Id = $"mmTestId{random.Next()}", - Pk = $"mmpk{random.Next()}" - }; - - ItemResponse createResponse = await container.CreateItemAsync(testItem); - itemsCleanupList.Add(testItem); - - Assert.AreEqual( - expected: HttpStatusCode.Created, - actual: createResponse.StatusCode); - - IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = createResponse.Diagnostics.GetContactedRegions(); - HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); - - Assert.AreEqual( - expected: HttpStatusCode.OK, - actual: createResponse.StatusCode); - - Assert.IsNotNull(contactedRegions); - - PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( - globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, - isReadOnlyOrMultiMaster: true); - - if (attemptCount > consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - if (attemptCount == consecutiveFailureCount) - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); - } - else - { - Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); - } - } - } - catch (CosmosException ce) - { - Assert.Fail("Create Item operation should succeed." + ce); - } - catch (Exception ex) - { - Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); - } - } - } - finally - { - Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); - Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); - - await this.TryDeleteItems(itemsList); - } - } private async Task TryCreateItems(List testItems) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs index 05f82bec36..3532b608aa 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs @@ -301,15 +301,6 @@ public async Task ReadItemAsync_WithThinClientEnabledAndServiceUnavailableReceiv containerName: containerName, containerRid: containerRid); - if (enablePartitionLevelFailover) - { - MockSetupsHelper.SetupPartitionKeyRanges( - mockHttpHandler: mockHttpHandler, - regionEndpoint: primaryRegionEndpoint, - containerResourceId: containerResourceId, - partitionKeyRangeIds: out IReadOnlyList secondaryRegionPartitionKeyRangeIds); - } - CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() { ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index 92d46ce910..1364409132 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -4,10 +4,8 @@ namespace Microsoft.Azure.Cosmos.Tests { - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; + using System; + using System.IO; using System.Net; using System.Net.Http; using System.Reflection; @@ -15,8 +13,7 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -75,7 +72,7 @@ public void TestCleanup() this.thinClientStoreModel?.Dispose(); } - [TestMethod] + [TestMethod] public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceResponse() { // Arrange @@ -255,7 +252,7 @@ public void Dispose_ShouldDisposeThinClientStoreClient() [TestMethod] public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() - { + { // Arrange MockThinClientStoreClient thinClientStoreClient = new MockThinClientStoreClient( (request, resourceType, uri, endpoint, globalDatabaseAccountName, clientCollectionCache, cancellationToken) => @@ -318,150 +315,12 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); - ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); - - // Act & Assert + ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); + + // Act & Assert await Assert.ThrowsExceptionAsync( async () => await storeModel.ProcessMessageAsync(request), "Expected 404 DocumentClientException from the final thinClientStore call"); - } - - [TestMethod] - public async Task PartitionLevelFailoverEnabled_ResolvesPartitionKeyRangeAndCallsLocationOverride() - { - Mock mockDocumentClient = new Mock(); - mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); - mockDocumentClient - .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new AccountProperties()); - - ConnectionPolicy connectionPolicy = new ConnectionPolicy(); - GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); - - Mock globalPartitionEndpointManager = new Mock(); - globalPartitionEndpointManager - .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) - .Returns(true) - .Verifiable(); - - ISessionContainer sessionContainer = new Mock().Object; - DocumentClientEventSource eventSource = new Mock().Object; - Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); - CosmosHttpClient httpClient = new Mock().Object; - - ThinClientStoreModel storeModel = new ThinClientStoreModel( - endpointManager, - globalPartitionEndpointManager.Object, - sessionContainer, - Cosmos.ConsistencyLevel.Session, - eventSource, - serializerSettings, - httpClient, - isPartitionLevelFailoverEnabled: true); - - Mock mockCollectionCache = new Mock( - sessionContainer, - storeModel, - null, - null, - null, - false); - - ContainerProperties containerProperties = new ContainerProperties("test", "/pk"); - typeof(ContainerProperties) - .GetProperty("ResourceId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public) - ?.SetValue(containerProperties, "testCollectionRid"); - containerProperties.PartitionKeyPath = "/pk"; - - mockCollectionCache - .Setup(c => c.ResolveCollectionAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(containerProperties); - - Mock mockPartitionKeyRangeCache = new Mock( - null, - storeModel, - mockCollectionCache.Object, - endpointManager, - false); - - PartitionKeyRange pkRange = new PartitionKeyRange { Id = "0", MinInclusive = "", MaxExclusive = "FF" }; - List pkRanges = new List { pkRange }; - IEnumerable> rangeTuples = pkRanges.Select(r => Tuple.Create(r, (ServiceIdentity)null)); - CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(rangeTuples, "testCollectionRid"); - - mockPartitionKeyRangeCache - .Setup(c => c.TryLookupAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(routingMap); - - storeModel.SetCaches(mockPartitionKeyRangeCache.Object, mockCollectionCache.Object); - - DocumentServiceRequest request = CreatePartitionedDocumentRequest(); - - MockThinClientStoreClient mockThinClientStoreClient = new MockThinClientStoreClient( - (DocumentServiceRequest req, ResourceType resourceType, Uri uri, Uri endpoint, string globalDatabaseAccountName, ClientCollectionCache clientCollectionCache, CancellationToken cancellationToken) => - { - MemoryStream stream = new MemoryStream(new byte[] { 1, 2, 3 }); - INameValueCollection headers = new StoreResponseNameValueCollection(); - return Task.FromResult(new DocumentServiceResponse(stream, headers, HttpStatusCode.OK)); - }); - - ReplaceThinClientStoreClientField(storeModel, mockThinClientStoreClient); - - // Act - await storeModel.ProcessMessageAsync(request); - - // Assert - globalPartitionEndpointManager.Verify(m => m.TryAddPartitionLevelLocationOverride(It.IsAny()), Times.Once()); - } - - [TestMethod] - public void CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() - { - Mock mockDocumentClient = new Mock(); - mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); - ConnectionPolicy connectionPolicy = new ConnectionPolicy(); - GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); - Mock globalPartitionEndpointManager = new Mock(); - globalPartitionEndpointManager - .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) - .Returns(true); - - globalPartitionEndpointManager - .Setup(m => m.TryMarkEndpointUnavailableForPartitionKeyRange(It.IsAny())) - .Returns(true) - .Verifiable(); - - globalPartitionEndpointManager - .Setup(m => m.IncrementRequestFailureCounterAndCheckIfPartitionCanFailover(It.IsAny())) - .Returns(true); - - ISessionContainer sessionContainer = new Mock().Object; - DocumentClientEventSource eventSource = new Mock().Object; - Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); - CosmosHttpClient httpClient = new Mock().Object; - - ThinClientStoreModel storeModel = new ThinClientStoreModel( - endpointManager, - globalPartitionEndpointManager.Object, - sessionContainer, - Cosmos.ConsistencyLevel.Session, - eventSource, - serializerSettings, - httpClient, - isPartitionLevelFailoverEnabled: true); - - TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); - - DocumentServiceRequest request = CreatePartitionedDocumentRequest(); - - for (int i = 0; i < 3; i++) - { - globalPartitionEndpointManager.Object.IncrementRequestFailureCounterAndCheckIfPartitionCanFailover(request); - } - - globalPartitionEndpointManager.Object.TryMarkEndpointUnavailableForPartitionKeyRange(request); - - globalPartitionEndpointManager.Verify(m => m.TryMarkEndpointUnavailableForPartitionKeyRange(It.IsAny()), Times.Once()); } private static void ReplaceThinClientStoreClientField(ThinClientStoreModel model, ThinClientStoreClient newClient) @@ -474,18 +333,6 @@ private static void ReplaceThinClientStoreClientField(ThinClientStoreModel model field.SetValue(model, newClient); } - private static DocumentServiceRequest CreatePartitionedDocumentRequest() - { - DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - "/dbs/test/colls/test/docs/test", - AuthorizationTokenType.PrimaryMasterKey); - request.Headers[HttpConstants.HttpHeaders.PartitionKey] = "[\"test\"]"; - request.RequestContext = new DocumentServiceRequestContext(); - return request; - } - internal class MockThinClientStoreClient : ThinClientStoreClient { private readonly Func> invokeAsyncFunc; From eff2071060ede9bcec68a0ffd6402414ebe064d6 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:29:39 -0700 Subject: [PATCH 05/20] Revert "Update ThinClientStoreModelTests.cs" This reverts commit dd9ba9c82553bfd36f88057fc3bfe099cca84290. --- .../ThinClientStoreModelTests.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index 1364409132..56726374fe 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -53,8 +53,7 @@ public void TestInitialize() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null, - chaosInterceptor: null); + httpClient: null); PartitionKeyRangeCache pkRangeCache = (PartitionKeyRangeCache)FormatterServices.GetUninitializedObject(typeof(PartitionKeyRangeCache)); @@ -122,8 +121,7 @@ public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceRespons defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null, - chaosInterceptor: null); + httpClient: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, @@ -204,8 +202,7 @@ public async Task ProcessMessageAsync_WithUnsupportedOperations_ShouldFallbackTo defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: mockCosmosHttpClient.Object, - chaosInterceptor: null); + httpClient: mockCosmosHttpClient.Object); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, @@ -295,8 +292,7 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null, - chaosInterceptor: null); + httpClient: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, From fd94d115c28dfb2cfb99de730858982e84fb62d2 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:32:55 -0700 Subject: [PATCH 06/20] Reapply "Update ThinClientStoreModelTests.cs" This reverts commit eff2071060ede9bcec68a0ffd6402414ebe064d6. --- .../ThinClientStoreModelTests.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index 56726374fe..1364409132 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -53,7 +53,8 @@ public void TestInitialize() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null); + httpClient: null, + chaosInterceptor: null); PartitionKeyRangeCache pkRangeCache = (PartitionKeyRangeCache)FormatterServices.GetUninitializedObject(typeof(PartitionKeyRangeCache)); @@ -121,7 +122,8 @@ public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceRespons defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null); + httpClient: null, + chaosInterceptor: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, @@ -202,7 +204,8 @@ public async Task ProcessMessageAsync_WithUnsupportedOperations_ShouldFallbackTo defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: mockCosmosHttpClient.Object); + httpClient: mockCosmosHttpClient.Object, + chaosInterceptor: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, @@ -292,7 +295,8 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() defaultConsistencyLevel: (Cosmos.ConsistencyLevel)this.defaultConsistencyLevel, eventSource: new DocumentClientEventSource(), serializerSettings: null, - httpClient: null); + httpClient: null, + chaosInterceptor: null); ClientCollectionCache clientCollectionCache = new Mock( this.sessionContainer, From e10bb61ff070c1d15e68b48f6c198f71d6eb3620 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:33:14 -0700 Subject: [PATCH 07/20] Reapply "Merge branch 'master' into users/nalutripician/FIThinclientPayloadFix" This reverts commit 5dde83f204a1bff4eb080818aa942a3a8eb57050. --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 14421 ++++++++-------- .../src/GatewayStoreModel.cs | 2 +- .../GlobalPartitionEndpointManagerCore.cs | 8 +- .../src/ThinClientStoreClient.cs | 339 +- .../src/ThinClientStoreModel.cs | 29 +- .../CosmosItemIntegrationTests.cs | 276 +- .../RegionFailoverTests.cs | 9 + .../ThinClientStoreModelTests.cs | 169 +- 8 files changed, 7860 insertions(+), 7393 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index 2a86cfeb92..d07329d231 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1,6849 +1,6850 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Security; - using System.Security; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using global::Azure.Core; - using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Query; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Telemetry; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tracing.TraceData; - using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Client; - using Microsoft.Azure.Documents.Collections; - using Microsoft.Azure.Documents.FaultInjection; - using Microsoft.Azure.Documents.Routing; - using Newtonsoft.Json; - using ResourceType = Documents.ResourceType; - - /// - /// Provides a client-side logical representation for the Azure Cosmos DB service. - /// This client is used to configure and execute requests against the service. - /// - /// - /// This type is thread safe. - /// - /// - /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. - /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. - /// - /// - /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. - /// Depending on your programming technique, you can do this in one of two ways: - /// - /// - /// - /// 1. By using a language construct such as the using statement in C#. - /// The using statement is actually a syntactic convenience. - /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. - /// - /// - /// - /// - /// - /// - /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. - /// The following example replaces the using block in the previous example with a try/catch/finally block. - /// - /// - /// - /// - /// - /// - internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal - { - private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; - private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; - private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; - private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; - private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; - private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; - private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; - private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; - private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; - private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; - private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; - private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; - private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; - private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; - private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; - // Env variable - private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; - - private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; - private const int DefaultMaxRequestsPerRntbdChannel = 30; - private const int DefaultRntbdPartitionCount = 1; - private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; - private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; - private const int DefaultRntbdPortPoolReuseThreshold = 256; - private const int DefaultRntbdPortPoolBindAttempts = 5; - private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; - private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; - private const bool DefaultEnableCpuMonitor = true; - private const string DefaultInitTaskKey = "InitTaskKey"; - - /// - /// Default thresholds for PPAF request hedging. +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Net.Security; + using System.Security; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Common; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Query; + using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Tracing.TraceData; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Client; + using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.FaultInjection; + using Microsoft.Azure.Documents.Routing; + using Newtonsoft.Json; + using ResourceType = Documents.ResourceType; + + /// + /// Provides a client-side logical representation for the Azure Cosmos DB service. + /// This client is used to configure and execute requests against the service. + /// + /// + /// This type is thread safe. + /// + /// + /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. + /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. + /// + /// + /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. + /// Depending on your programming technique, you can do this in one of two ways: + /// + /// + /// + /// 1. By using a language construct such as the using statement in C#. + /// The using statement is actually a syntactic convenience. + /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. + /// + /// + /// + /// + /// + /// + /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. + /// The following example replaces the using block in the previous example with a try/catch/finally block. + /// + /// + /// + /// + /// + /// + internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal + { + private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; + private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; + private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; + private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; + private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; + private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; + private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; + private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; + private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; + private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; + private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; + private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; + private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; + private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; + private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; + // Env variable + private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; + + private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; + private const int DefaultMaxRequestsPerRntbdChannel = 30; + private const int DefaultRntbdPartitionCount = 1; + private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; + private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; + private const int DefaultRntbdPortPoolReuseThreshold = 256; + private const int DefaultRntbdPortPoolBindAttempts = 5; + private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; + private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; + private const bool DefaultEnableCpuMonitor = true; + private const string DefaultInitTaskKey = "InitTaskKey"; + + /// + /// Default thresholds for PPAF request hedging. + /// + private const int DefaultHedgingThresholdInMilliseconds = 1000; + private const int DefaultHedgingThresholdStepInMilliseconds = 500; + + private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; + private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; + + private readonly bool IsLocalQuorumConsistency = false; + private readonly bool isReplicaAddressValidationEnabled; + private readonly bool enableAsyncCacheExceptionNoSharing; + + private readonly bool isThinClientEnabled; + + //Fault Injection + private readonly IChaosInterceptorFactory chaosInterceptorFactory; + private readonly IChaosInterceptor chaosInterceptor; + + private bool isChaosInterceptorInititalized = false; + + //Auth + internal readonly AuthorizationTokenProvider cosmosAuthorization; + + // Gateway has backoff/retry logic to hide transient errors. + private RetryPolicy retryPolicy; + private bool allowOverrideStrongerConsistency = false; + private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; + private int openConnectionTimeoutInSeconds = 5; + private int idleConnectionTimeoutInSeconds = -1; + private int timerPoolGranularityInSeconds = 1; + private bool enableRntbdChannel = true; + private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; + private int rntbdPartitionCount = DefaultRntbdPartitionCount; + private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; + private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; + private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; + private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + private bool enableCpuMonitor = DefaultEnableCpuMonitor; + private int rntbdMaxConcurrentOpeningConnectionCount = 5; + private string clientId; + + //Consistency + private Documents.ConsistencyLevel? desiredConsistencyLevel; + + internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } + + internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } + + private ClientCollectionCache collectionCache; + + private PartitionKeyRangeCache partitionKeyRangeCache; + + //Private state. + private bool isSuccessfullyInitialized; + private bool isDisposed; + + // creator of TransportClient is responsible for disposing it. + private IStoreClientFactory storeClientFactory; + internal CosmosHttpClient httpClient { get; private set; } + + // Flag that indicates whether store client factory must be disposed whenever client is disposed. + // Setting this flag to false will result in store client factory not being disposed when client is disposed. + // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. + private bool isStoreClientFactoryCreatedInternally; + + //Id counter. + private static int idCounter; + //Trace Id. + private int traceId; + + //RemoteCertificateValidationCallback + internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; + + //Distributed Tracing Flag + internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; + + //SessionContainer. + internal ISessionContainer sessionContainer; + + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + private AsyncLazy queryPartitionProvider; + + private DocumentClientEventSource eventSource; + private Func> initializeTaskFactory; + internal AsyncCacheNonBlocking initTaskCache; + + private JsonSerializerSettings serializerSettings; + private event EventHandler sendingRequest; + private event EventHandler receivedResponse; + private Func transportClientHandlerFactory; + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + { + if (authKey == null) + { + throw new ArgumentNullException("authKey"); + } + + if (authKey != null) + { + this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); + } + + this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); + this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The connection policy for the client. + /// + /// + /// This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + HttpMessageHandler handler, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) + { + } + + internal DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null) + : this(serviceEndpoint, + AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), + sendingRequestEventArgs, + connectionPolicy, + desiredConsistencyLevel, + serializerSettings, + apitype, + receivedResponseEventArgs, + handler, + sessionContainer, + enableCpuMonitor, + transportClientHandlerFactory, + storeClientFactory) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The cosmos authorization for the client. + /// The event handler to be invoked before the request is sent. + /// The event handler to be invoked after a response has been received. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// Api type for the account + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// The default session container with which DocumentClient is created. + /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. + /// Transport client handler factory. + /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. + /// Flag to allow Quorum Read with Eventual Consistency Account + /// + /// This delegate responsible for validating the third party certificate. + /// This is distributed tracing flag + /// This is the chaos interceptor used for fault injection + /// A boolean flag indicating if stack trace optimization is enabled. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + internal DocumentClient(Uri serviceEndpoint, + AuthorizationTokenProvider cosmosAuthorization, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null, + bool isLocalQuorumConsistency = false, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + IChaosInterceptorFactory chaosInterceptorFactory = null, + bool enableAsyncCacheExceptionNoSharing = true) + { + if (sendingRequestEventArgs != null) + { + this.sendingRequest += sendingRequestEventArgs; + } + + if (serializerSettings != null) + { + this.serializerSettings = serializerSettings; + } + + this.ApiType = apitype; + + if (receivedResponseEventArgs != null) + { + this.receivedResponse += receivedResponseEventArgs; + } + + this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; + this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); + this.transportClientHandlerFactory = transportClientHandlerFactory; + this.IsLocalQuorumConsistency = isLocalQuorumConsistency; + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.chaosInterceptorFactory = chaosInterceptorFactory; + this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); + this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + + this.Initialize( + serviceEndpoint: serviceEndpoint, + connectionPolicy: connectionPolicy, + desiredConsistencyLevel: desiredConsistencyLevel, + handler: handler, + sessionContainer: sessionContainer, + enableCpuMonitor: enableCpuMonitor, + storeClientFactory: storeClientFactory, + cosmosClientId: cosmosClientId, + remoteCertificateValidationCallback: remoteCertificateValidationCallback, + cosmosClientTelemetryOptions: cosmosClientTelemetryOptions, + enableThinClientMode: this.isThinClientEnabled); + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The connection policy for the client. + /// The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Internal constructor purely for unit-testing + /// + internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) + { + // do nothing + this.ServiceEndpoint = serviceEndpoint; + this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); + } + + internal virtual async Task GetCollectionCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.collectionCache; + } + } + + internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.partitionKeyRangeCache; + } + } + + internal GlobalAddressResolver AddressResolver { get; private set; } + + internal GlobalEndpointManager GlobalEndpointManager { get; private set; } + + internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } + + /// + /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. + /// + /// + /// A object. + /// + /// + /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. + /// If there are any initialization exceptions, this method will throw them (set on the task). + /// Alternately, calling any API will throw initialization exception at the first call. + /// + /// + /// + /// + /// + /// + public Task OpenAsync(CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); + } + + private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); + } + + private async Task OpenPrivateAsync(CancellationToken cancellationToken) + { + // Initialize caches for all databases and collections + ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( + new FeedOptions { MaxItemCount = -1 }); + + try + { + while (databaseFeedReader.HasMoreResults) + { + foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) + { + ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( + database.SelfLink, + new FeedOptions { MaxItemCount = -1 }); + List tasks = new List(); + while (collectionFeedReader.HasMoreResults) + { + tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); + } + + await Task.WhenAll(tasks); + } + } + } + catch (DocumentClientException ex) + { + // Clear the caches to ensure that we don't have partial results + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + + DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); + } + } + + internal virtual void Initialize(Uri serviceEndpoint, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + IStoreClientFactory storeClientFactory = null, + TokenCredential tokenCredential = null, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + bool enableThinClientMode = false) + { + if (serviceEndpoint == null) + { + throw new ArgumentNullException("serviceEndpoint"); + } + + this.clientId = cosmosClientId; + this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; + this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); + + this.queryPartitionProvider = new AsyncLazy(async () => + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); + }, CancellationToken.None); + +#if !(NETSTANDARD15 || NETSTANDARD16) +#if NETSTANDARD20 + // GetEntryAssembly returns null when loaded from native netstandard2.0 + if (System.Reflection.Assembly.GetEntryAssembly() != null) + { +#endif + // For tests we want to allow stronger consistency during construction or per call + string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; + if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) + { + if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) + { + this.allowOverrideStrongerConsistency = false; + } + } + + // We might want to override the defaults sometime + string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; + if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) + { + int maxConcurrentConnectionOpenRequestOverrideInt = 0; + if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) + { + this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; + } + } + + string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) + { + int openConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) + { + this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; + } + } + + string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) + { + int idleConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) + { + this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; + } + } + + string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; + if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) + { + int timerPoolGranularityInSecondsOverrideInt = 0; + if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) + { + // timeoutgranularity specified should be greater than min(5 seconds) + if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) + { + this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; + } + } + } + + string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; + if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) + { + bool enableRntbdChannel = false; + if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) + { + this.enableRntbdChannel = enableRntbdChannel; + } + } + + string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; + if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) + { + int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; + if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) + { + this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; + } + } + + string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; + if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) + { + int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; + if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) + { + this.rntbdPartitionCount = rntbdPartitionCount; + } + } + + string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; + if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) + { + int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) + { + this.maxRntbdChannels = maxRntbdChannels; + } + } + + string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; + if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) + { + PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; + if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) + { + this.rntbdPortReuseMode = portReuseMode; + } + } + + string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; + if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) + { + int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; + if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) + { + this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; + } + } + + string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; + if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) + { + int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; + if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) + { + this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; + } + } + + string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) + { + int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) + { + this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; + } + } + + string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) + { + int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) + { + this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; + } + } + + if (enableCpuMonitor.HasValue) + { + this.enableCpuMonitor = enableCpuMonitor.Value; + } + else + { + string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; + if (!string.IsNullOrEmpty(enableCpuMonitorString)) + { + bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; + if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) + { + this.enableCpuMonitor = enableCpuMonitorFlag; + } + } + } +#if NETSTANDARD20 + } +#endif +#endif + + string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); + if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) + { + if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) + { + if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) + { + throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); + } + + this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; + } + } + + // ConnectionPolicy always overrides appconfig + if (connectionPolicy != null) + { + if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) + { + this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) + { + this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) + { + this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; + } + + if (connectionPolicy.MaxTcpPartitionCount.HasValue) + { + this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; + } + + if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) + { + this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; + } + + if (connectionPolicy.PortReuseMode.HasValue) + { + this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; + } + } + + this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); + + this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; + +#if !NETSTANDARD16 + if (ServicePointAccessor.IsSupported) + { + ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); + servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; + } +#endif + this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); + + this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( + this.ApiType, + DocumentClientEventSource.Instance, + this.ConnectionPolicy, + handler, + this.sendingRequest, + this.receivedResponse, + this.chaosInterceptor); + + // Loading VM Information (non blocking call and initialization won't fail if this call fails) + VmMetadataApiHandler.TryInitialize(this.httpClient); + + if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) + { + CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); + CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); + + CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); + } + + // Starting ClientTelemetry Job + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, + this.ConnectionPolicy, + this.cosmosAuthorization, + this.httpClient, + this.ServiceEndpoint, + this.GlobalEndpointManager, + this.cancellationTokenSource, + this.chaosInterceptor is not null); + + if (sessionContainer != null) + { + this.sessionContainer = sessionContainer; + } + else + { + this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); + } + + this.desiredConsistencyLevel = desiredConsistencyLevel; + // Setup the proxy to be used based on connection mode. + // For gateway: GatewayProxy. + // For direct: WFStoreProxy [set in OpenAsync()]. + this.eventSource = DocumentClientEventSource.Instance; + + this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( + () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), + new ResourceThrottleRetryPolicy( + this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, + this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); + + // Create the task to start the initialize task + // Task will be awaited on in the EnsureValidClientAsync + Task initTask = this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + + // ContinueWith on the initialization task is needed for handling the UnobservedTaskException + // if this task throws for some reason. Awaiting inside a constructor is not supported and + // even if we had to await inside GetInitializationTask to catch the exception, that will + // be a blocking call. In such cases, the recommended approach is to "handle" the + // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted + // and accessing the Exception property on the target task. +#pragma warning disable VSTHRD110 // Observe result of async calls +#pragma warning disable CDX1000 // DontConvertExceptionToObject + initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); +#pragma warning restore CDX1000 // DontConvertExceptionToObject +#pragma warning restore VSTHRD110 // Observe result of async calls + + this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); + DefaultTrace.TraceInformation(string.Format( + CultureInfo.InvariantCulture, + "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", + this.traceId, + serviceEndpoint.ToString(), + this.ConnectionPolicy.ConnectionMode.ToString(), + this.ConnectionPolicy.ConnectionProtocol.ToString(), + desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); + + this.QueryCompatibilityMode = QueryCompatibilityMode.Default; + } + + // Always called from under the lock except when called from Intilialize method during construction. + private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) + { + await this.InitializeGatewayConfigurationReaderAsync(); + + if (this.desiredConsistencyLevel.HasValue) + { + this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); + } + + if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride + && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) + { + this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; + } + + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; + this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); + this.InitializePartitionLevelFailoverWithDefaultHedging(); + + this.PartitionKeyRangeLocation = + this.ConnectionPolicy.EnablePartitionLevelFailover + || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker + ? new GlobalPartitionEndpointManagerCore( + this.GlobalEndpointManager, + this.ConnectionPolicy.EnablePartitionLevelFailover, + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) + : GlobalPartitionEndpointManagerNoOp.Instance; + + this.retryPolicy = new RetryPolicy( + globalEndpointManager: this.GlobalEndpointManager, + connectionPolicy: this.ConnectionPolicy, + partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation); + + this.ResetSessionTokenRetryPolicy = this.retryPolicy; + + GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( + this.GlobalEndpointManager, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + this.PartitionKeyRangeLocation, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + + this.GatewayStoreModel = gatewayStoreModel; + + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); + + gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + + if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway && this.isThinClientEnabled) + { + ThinClientStoreModel thinClientStoreModel = new ( + endpointManager: this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, + chaosInterceptor: this.chaosInterceptor); + + thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + + this.StoreModel = thinClientStoreModel; + } + else if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) + { + this.StoreModel = this.GatewayStoreModel; + } + else + { + this.InitializeDirectConnectivity(storeClientFactory); + } + + return true; + } + + private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) + { + if (databaseName == null) + { + throw new ArgumentNullException(nameof(databaseName)); + } + + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + using ( + DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Query, + ResourceType.Document, + collection.SelfLink, + AuthorizationTokenType.PrimaryMasterKey)) + { + ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( + resolvedCollection.ResourceId, + new Range( + PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, + PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, + true, + false), + NoOpTrace.Singleton); + + // In Gateway mode, AddressCache is null + if (this.AddressResolver != null) + { + await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); + } + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. + /// + /// + /// + /// The session object used for version tracking when the consistency level is set to Session. + /// + /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. + /// + public object Session + { + get + { + return this.sessionContainer; + } + + set + { + SessionContainer container = value as SessionContainer; + if (container == null) + { + throw new ArgumentNullException("value"); + } + + if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + ClientResources.BadSession, + container.HostName, + this.ServiceEndpoint.Host)); + } + + SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; + if (currentSessionContainer == null) + { + throw new ArgumentNullException(nameof(currentSessionContainer)); + } + + currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. + /// + /// Collection for which session token must be retrieved. + /// + /// The session token used for version tracking when the consistency level is set to Session. + /// + /// + /// The session token can be saved and supplied to a request via . + /// + internal string GetSessionToken(string collectionLink) + { + SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; + + if (sessionContainerInternal == null) + { + throw new ArgumentNullException(nameof(sessionContainerInternal)); + } + + return sessionContainerInternal.GetSessionToken(collectionLink); + } + + /// + /// Gets the Api type + /// + internal ApiType ApiType + { + get; private set; + } + + internal bool UseMultipleWriteLocations { get; private set; } + + /// + /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. + /// + /// + /// The Uri for the service endpoint. + /// + /// + public Uri ServiceEndpoint + { + get; + private set; + } + + /// + /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri WriteEndpoint + { + get + { + return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri ReadEndpoint + { + get + { + return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the Connection policy used by the client from the Azure Cosmos DB service. + /// + /// + /// The Connection policy used by the client. + /// + /// + public ConnectionPolicy ConnectionPolicy { get; private set; } + + /// + /// Gets the AuthKey used by the client from the Azure Cosmos DB service. + /// + /// + /// The AuthKey used by the client. + /// + /// + public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); + + /// + /// Gets the configured consistency level of the client from the Azure Cosmos DB service. + /// + /// + /// The configured of the client. + /// + /// + public virtual Documents.ConsistencyLevel ConsistencyLevel + { + get + { +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : + this.accountServiceConfiguration.DefaultConsistencyLevel; + } + } + + /// + /// Returns the account properties available in the service configuration if the client was initialized. + /// + public bool TryGetCachedAccountProperties(out AccountProperties properties) + { + if (this.isSuccessfullyInitialized + && this.accountServiceConfiguration != null + && this.accountServiceConfiguration.AccountProperties != null) + { + properties = this.accountServiceConfiguration.AccountProperties; + return true; + } + + properties = null; + return false; + } + + /// + /// Disposes the client for the Azure Cosmos DB service. + /// + /// + /// + /// + /// + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + if (this.telemetryToServiceHelper != null) + { + this.telemetryToServiceHelper.Dispose(); + this.telemetryToServiceHelper = null; + } + + if (!this.cancellationTokenSource.IsCancellationRequested) + { + this.cancellationTokenSource.Cancel(); + } + + this.cancellationTokenSource.Dispose(); + + if (this.StoreModel != null) + { + this.StoreModel.Dispose(); + this.StoreModel = null; + } + + if (this.storeClientFactory != null) + { + // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference + if (this.isStoreClientFactoryCreatedInternally) + { + this.storeClientFactory.Dispose(); + } + + this.storeClientFactory = null; + } + + if (this.AddressResolver != null) + { + this.AddressResolver.Dispose(); + this.AddressResolver = null; + } + + if (this.httpClient != null) + { + try + { + this.httpClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.httpClient = null; + } + + if (this.cosmosAuthorization != null) + { + this.cosmosAuthorization.Dispose(); + } + + if (this.GlobalEndpointManager != null) + { + this.GlobalEndpointManager.Dispose(); + this.GlobalEndpointManager = null; + } + + if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) + { + this.queryPartitionProvider.Value.Dispose(); + } + + if (this.initTaskCache != null) + { + this.initTaskCache.Dispose(); + this.initTaskCache = null; + } + + DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); + DefaultTrace.Flush(); + + this.isDisposed = true; + } + + //Compatibility mode: + // Allows to specify compatibility mode used by client when making query requests. + // should be removed when application/sql is no longer supported. + internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } + + /// + /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). + /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache + /// to resolves the request's collection name. If it differs from the session container's resource id it + /// explains the session unavailable exception: somebody removed and recreated the collection. In this + /// case we retry once again (with empty session token) otherwise we return the error to the client + /// (see RenameCollectionAwareClientRetryPolicy) + /// + internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } + + /// + /// Gets and sets the IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension StoreModel { get; set; } + + /// + /// Gets and sets the gateway IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension GatewayStoreModel { get; set; } + + /// + /// Gets and sets on execute scalar query callback + /// + /// + /// Test hook to enable unit test for scalar queries + /// + internal Action OnExecuteScalarQueryCallback { get; set; } + + internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; + + internal virtual async Task GetDefaultConsistencyLevelAsync() + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; + } + + internal Task GetDesiredConsistencyLevelAsync() + { + return Task.FromResult(this.desiredConsistencyLevel); + } + + internal async Task ProcessRequestAsync( + string verb, + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + string testAuthorization = null) // Only for unit-tests + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (verb == null) + { + throw new ArgumentNullException(nameof(verb)); + } + + (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( + request.ResourceAddress, + PathsHelper.GetResourcePath(request.ResourceType), + verb, + request.Headers, + AuthorizationTokenType.PrimaryMasterKey); + + // Unit-test hook + if (testAuthorization != null) + { + payload = testAuthorization; + authorization = testAuthorization; + } + request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; + + try + { + return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); + } + catch (DocumentClientException dce) + { + this.cosmosAuthorization.TraceUnauthorized( + dce, + authorization, + payload); + + throw; + } + } + + internal Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); + } + + internal async Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + ITrace trace, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(trace); + + retryPolicyInstance?.OnBeforeSendRequest(request); + + using (new ActivityScope(Guid.NewGuid())) + { + IStoreModel storeProxy = this.GetStoreProxy(request); + return await storeProxy.ProcessMessageAsync(request, cancellationToken); + } + } + + /// + /// Establishes and Initializes the Rntbd connection to all the backend replica nodes + /// for the given database name and container. + /// + /// A string containing the cosmos database name. + /// A string containing the cosmos container link uri. + /// An instance of the . + internal async Task OpenConnectionsToAllReplicasAsync( + string databaseName, + string containerLinkUri, + CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(databaseName) || + string.IsNullOrEmpty(containerLinkUri)) + { + string resourceName = string.IsNullOrEmpty(databaseName) ? + nameof(databaseName) : + nameof(containerLinkUri); + + throw new ArgumentNullException(resourceName); + } + + if (this.StoreModel != null) + { + try + { + await this.StoreModel.OpenConnectionsToAllReplicasAsync( + databaseName, + containerLinkUri, + cancellationToken); + } + catch (Exception) + { + throw; + } + } + } + + private static string NormalizeAuthorizationPayload(string input) + { + const int expansionBuffer = 12; + StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); + for (int i = 0; i < input.Length; i++) + { + switch (input[i]) + { + case '\n': + builder.Append("\\n"); + break; + case '/': + builder.Append("\\/"); + break; + default: + builder.Append(input[i]); + break; + } + } + + return builder.ToString(); + } + + internal async Task InitilizeFaultInjectionAsync() + { + if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) + { + this.isChaosInterceptorInititalized = true; + await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); + } + } + + internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) + { + return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.maxRequestsPerRntbdChannel, + this.maxRntbdChannels, + this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + this.rntbdPortReuseMode); + } + + internal virtual async Task EnsureValidClientAsync(ITrace trace) + { + if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) + { + return; + } + + // Trace when the Initialization of client has not been completed. Usually during first call + using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) + { + // If the initialization task failed, we should retry initialization. + // We may end up throwing the same exception but this will ensure that we dont have a + // client which is unusable and can resume working if it failed initialization once. + // If we have to reinitialize the client, it needs to happen in thread safe manner so that + // we dont re-initalize the task again for each incoming call. + try + { + this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + } + catch (DocumentClientException ex) + { + throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( + dce: ex, + trace: trace); + } + catch (Exception e) + { + DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); + childTrace.AddDatum("initializeTask failed", e.Message); + throw; + } + + await this.InitilizeFaultInjectionAsync(); + } + } + + #region Create Impl + /// + /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed. + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); + } + + private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, + Documents.Client.RequestOptions options) + { + if (database == null) + { + throw new ArgumentNullException("database"); + } + + // Doing a Read before Create will give us better latency for existing databases + try + { + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDatabaseAsync(database, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + + /// + /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to create. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// Finally, a Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Create a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, + object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, + CancellationToken cancellationToken = default) + { + // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy); + } + + private async Task> CreateDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 collection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDocumentCollectionPrivateAsync( + string databaseLink, + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse( + await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 DocumentCollection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); + } + + private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( + string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) + { + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make + // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. + Documents.Database database = await this.ReadDatabaseAsync(databaseLink); + + // Doing a Read before Create will give us better latency for existing collections. + // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, + // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. + try + { + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + + /// + /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link to the source object. + /// The target object. + /// (optional)The point in time to restore. If null, use the latest restorable time. + /// (Optional) The for the request. + /// The task object representing the service response for the asynchronous operation. + internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + if (targetDocumentCollection == null) + { + throw new ArgumentNullException("targetDocumentCollection"); + } + + bool isFeed; + string resourceTypeString; + string resourceIdOrFullName; + bool isNameBased; + + string dbsId; + string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); + if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + dbsId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + string sourceCollId; + if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + sourceCollId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + this.ValidateResource(targetDocumentCollection); + + if (options == null) + { + options = new Documents.Client.RequestOptions(); + } + if (!options.RemoteStorageType.HasValue) + { + options.RemoteStorageType = RemoteStorageType.Standard; + } + options.SourceDatabaseId = dbsId; + options.SourceCollectionId = sourceCollId; + if (restoreTime.HasValue) + { + options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + targetDocumentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Get the status of a collection being restored in the Azure Cosmos DB service. + /// + /// The link of the document collection being restored. + /// The task object representing the service response for the asynchronous operation. + internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (string.IsNullOrEmpty(targetDocumentCollectionLink)) + { + throw new ArgumentNullException("targetDocumentCollectionLink"); + } + + ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( + targetDocumentCollectionLink, + new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, + retryPolicyInstance); + string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); + if (restoreState == null) + { + restoreState = RestoreState.RestoreCompleted.ToString(); + } + + DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() + { + State = restoreState + }; + + return ret; + } + + /// + /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ + /// The object to create. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the created object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. + /// + /// + /// 409 + /// + /// Conflict - This means a with an id matching the id field of already existed, + /// or there is already a pending snapshot for the specified resource link. + /// + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that + /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (snapshot == null) + { + throw new ArgumentNullException("snapshot"); + } + + this.ValidateResource(snapshot); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Snapshots_Root, + snapshot, + ResourceType.Snapshot, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Delete Impl + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. snapshots/snapshot_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Replace Impl + /// + /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. + /// + /// the updated document collection. + /// the request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceDocumentCollectionPrivateAsync( + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(documentCollection), + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + // set the session token + if (collection.Resource != null) + { + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + } + return collection; + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If either or is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now dynamically cast doc back to your MyPoco + /// MyPoco poco = (dynamic)doc; + /// + /// //Update some properties of the poco object + /// poco.MyProperty = "updated value"; + /// + /// //Now persist these changes to the database using doc.SelLink and the update poco object + /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if ((options == null) || (options.PartitionKey == null)) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible( + () => this.ReplaceDocumentPrivateAsync( + documentLink, + document, + options, + requestRetryPolicy, + cancellationToken), + requestRetryPolicy, + cancellationToken); + } + + private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// doc.SetPropertyValue("MyProperty", "updated value"); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Document updated = await client.ReplaceDocumentAsync(doc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( + this.GetLinkForRouting(document), + document, + options, + retryPolicyInstance, + cancellationToken), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + this.ValidateResource(document); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + documentLink, + document, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, document, options); + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "sproc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// sproc.Body = "function () {new javascript body for sproc}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceStoredProcedurePrivateAsync( + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(storedProcedure), + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "trigger id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// trigger.Body = "function () {new javascript body for trigger}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// Trigger updated = await client.ReplaceTriggerAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(trigger), + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "udf id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// udf.Body = "function () {new javascript body for udf}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedFunctionPrivateAsync( + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(function), + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. + /// + /// + /// + /// + /// + /// r.ResourceLink == "collection selfLink") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Create a new offer with the changed throughput + /// OfferV2 newOffer = new OfferV2(offer, 5000); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Offer updated = await client.ReplaceOfferAsync(newOffer); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceOfferAsync(Offer offer) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (offer == null) + { + throw new ArgumentNullException("offer"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + offer.SelfLink, + offer, + ResourceType.Offer, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse( + await this.UpdateAsync(request, retryPolicyInstance), + OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "user defined type id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(userDefinedType), + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Read Impl + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Database resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// (docLink); + /// ]]> + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the DocumentCollection to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionPrivateAsync( + string documentCollectionLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the stored procedure to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Trigger to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the User Defined Function to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Conflict to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads an from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Offer to be read. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. + /// + /// + /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about + /// minimum throughput of a Cosmos container (or a database) + /// To retrieve the minimum throughput for a collection/database, use the following sample + /// + /// response = await client.ReadOfferAsync(offer.SelfLink); + /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; + /// ]]> + /// + /// + /// + /// + /// + /// + /// + /// + public Task> ReadOfferAsync(string offerLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(offerLink)) + { + throw new ArgumentNullException("offerLink"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Offer, + offerLink, + null, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Reads a as an asynchronous operation. + /// + /// The link for the schema to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Schema are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentSchemaLink)) + { + throw new ArgumentNullException("documentSchemaLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Schema, + documentSchemaLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the UserDefinedType resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypeLink)) + { + throw new ArgumentNullException("userDefinedTypeLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedType, + userDefinedTypeLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Snapshot resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Snapshot are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/snapshots/{snapshot identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region ReadFeed Impl + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDatabaseFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = null; + /// List ids = new List(); + /// do + /// { + /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); + /// foreach (var item in response) + /// { + /// ids.Add(item.Id); + /// } + /// } + /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(partitionKeyRangesLink)) + { + throw new ArgumentNullException("partitionKeyRangesLink"); + } + + return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionsLink)) + { + throw new ArgumentNullException("collectionsLink"); + } + + return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProceduresLink)) + { + throw new ArgumentNullException("storedProceduresLink"); + } + + return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggersLink)) + { + throw new ArgumentNullException("triggersLink"); + } + + return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedFunctionsLink)) + { + throw new ArgumentNullException("userDefinedFunctionsLink"); + } + + return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. + /// This takes returns a which will contain an enumerable list of dynamic objects. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a containing dynamic objects representing the items in the feed. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. + /// This is important becuse a DocumentCollection can contain different kinds of documents. + /// + /// + /// + /// + public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentsLink)) + { + throw new ArgumentNullException("documentsLink"); + } + + DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); + return new DocumentFeedResponse( + response.Cast(), + response.Count, + response.Headers, + response.UseETagAsContinuation, + response.QueryMetrics, + response.RequestStatistics, + responseLengthBytes: response.ResponseLengthBytes); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); + } + + private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictsLink)) + { + throw new ArgumentNullException("conflictsLink"); + } + + return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service + /// as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadOfferAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadOffersFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionSchemaLink)) + { + throw new ArgumentNullException("documentCollectionSchemaLink"); + } + + return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypesLink)) + { + throw new ArgumentNullException("userDefinedTypesLink"); + } + + return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a set of containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadSnapshotFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); + } + + #endregion + + #region Stored procs + /// + /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) + { + return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + default, + procedureParams), + retryPolicyInstance); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + cancellationToken, + procedureParams), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ExecuteStoredProcedurePrivateAsync( + string storedProcedureLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + params dynamic[] procedureParams) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); + string storedProcedureInput = serializerSettings == null ? + JsonConvert.SerializeObject(procedureParams) : + JsonConvert.SerializeObject(procedureParams, serializerSettings); + using (MemoryStream storedProcedureInputStream = new MemoryStream()) + { + using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) + { + await writer.WriteAsync(storedProcedureInput); + await writer.FlushAsync(); + storedProcedureInputStream.Position = 0; + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.ExecuteJavaScript, + ResourceType.StoredProcedure, + storedProcedureLink, + storedProcedureInputStream, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); + if (options?.PartitionKeyRangeId == null) + { + await this.AddPartitionKeyInformationAsync( + request, + options); + } + + retryPolicyInstance?.OnBeforeSendRequest(request); + + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new StoredProcedureResponse(await this.ExecuteProcedureAsync( + request, + retryPolicyInstance, + cancellationToken), + this.GetSerializerSettingsForRequest(options)); + } + } + } + } + + #endregion + + #region Upsert Impl + /// + /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to upsert. + /// (Optional) The for the request. + /// The that was upserted within a task object representing the service response for the asynchronous operation. + /// If is not set + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// + /// + /// The example below upserts a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to upsert. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// A Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Upsert a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) + { + // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy, cancellationToken); + } + + private async Task> UpsertDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + // To be implemented. + throw new NotImplementedException(); + } + + /// + /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. + /// + /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ + /// The object to upsert. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the upserted object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + #endregion + + #region IAuthorizationTokenProvider + + ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType) + { + return this.cosmosAuthorization.GetUserAuthorizationAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType); + } + + ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType, + ITrace trace) + { + return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType, + trace); + } + + Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( + DocumentServiceRequest request, + string federationId, + string verb, + string resourceId) + { + return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( + request, + federationId, + verb, + resourceId); + } + + #endregion + + #region Core Implementation + internal Task CreateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpdateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); + } + + internal Task ReadAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task ReadFeedAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task DeleteAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteProcedureAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteQueryAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpsertAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + #endregion + + /// + /// Read the from the Azure Cosmos DB service as an asynchronous operation. + /// + /// + /// A wrapped in a object. + /// + public Task GetDatabaseAccountAsync() + { + return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); + } + + /// + /// Read the as an asynchronous operation + /// given a specific reginal endpoint url. + /// + /// The reginal url of the serice endpoint. + /// The CancellationToken + /// + /// A wrapped in a object. + /// + Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) + { + return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); + } + + private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) + { + async ValueTask CreateRequestMessage() + { + HttpRequestMessage request = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = serviceEndpoint + }; + + INameValueCollection headersCollection = new StoreResponseNameValueCollection(); + await this.cosmosAuthorization.AddAuthorizationHeaderAsync( + headersCollection, + serviceEndpoint, + "GET", + AuthorizationTokenType.PrimaryMasterKey); + + foreach (string key in headersCollection.AllKeys()) + { + request.Headers.Add(key, headersCollection[key]); + } + + return request; + } + + AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, + clientSideRequestStatistics: null); + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; + + if (this.queryPartitionProvider.IsValueCreated) + { + (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); + } + + return databaseAccount; + } + + return null; + } + + #region Private Impl + + /// + /// Certain requests must be routed through gateway even when the client connectivity mode is direct. + /// For e.g., DocumentCollection creation. This method returns the based + /// on the input . /// - private const int DefaultHedgingThresholdInMilliseconds = 1000; - private const int DefaultHedgingThresholdStepInMilliseconds = 500; - - private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; - private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; - - private readonly bool IsLocalQuorumConsistency = false; - private readonly bool isReplicaAddressValidationEnabled; - private readonly bool enableAsyncCacheExceptionNoSharing; - - private readonly bool isThinClientEnabled; - - //Fault Injection - private readonly IChaosInterceptorFactory chaosInterceptorFactory; - private readonly IChaosInterceptor chaosInterceptor; - - private bool isChaosInterceptorInititalized = false; - - //Auth - internal readonly AuthorizationTokenProvider cosmosAuthorization; - - // Gateway has backoff/retry logic to hide transient errors. - private RetryPolicy retryPolicy; - private bool allowOverrideStrongerConsistency = false; - private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; - private int openConnectionTimeoutInSeconds = 5; - private int idleConnectionTimeoutInSeconds = -1; - private int timerPoolGranularityInSeconds = 1; - private bool enableRntbdChannel = true; - private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; - private int rntbdPartitionCount = DefaultRntbdPartitionCount; - private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; - private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; - private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; - private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - private bool enableCpuMonitor = DefaultEnableCpuMonitor; - private int rntbdMaxConcurrentOpeningConnectionCount = 5; - private string clientId; - - //Consistency - private Documents.ConsistencyLevel? desiredConsistencyLevel; - - internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } - - internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } - - private ClientCollectionCache collectionCache; - - private PartitionKeyRangeCache partitionKeyRangeCache; - - //Private state. - private bool isSuccessfullyInitialized; - private bool isDisposed; - - // creator of TransportClient is responsible for disposing it. - private IStoreClientFactory storeClientFactory; - internal CosmosHttpClient httpClient { get; private set; } - - // Flag that indicates whether store client factory must be disposed whenever client is disposed. - // Setting this flag to false will result in store client factory not being disposed when client is disposed. - // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. - private bool isStoreClientFactoryCreatedInternally; - - //Id counter. - private static int idCounter; - //Trace Id. - private int traceId; - - //RemoteCertificateValidationCallback - internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; - - //Distributed Tracing Flag - internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; - - //SessionContainer. - internal ISessionContainer sessionContainer; - - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - - private AsyncLazy queryPartitionProvider; - - private DocumentClientEventSource eventSource; - private Func> initializeTaskFactory; - internal AsyncCacheNonBlocking initTaskCache; - - private JsonSerializerSettings serializerSettings; - private event EventHandler sendingRequest; - private event EventHandler receivedResponse; - private Func transportClientHandlerFactory; - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - { - if (authKey == null) - { - throw new ArgumentNullException("authKey"); - } - - if (authKey != null) - { - this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); - } - - this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); - this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The connection policy for the client. - /// - /// - /// This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - HttpMessageHandler handler, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) - { - } - - internal DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null) - : this(serviceEndpoint, - AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), - sendingRequestEventArgs, - connectionPolicy, - desiredConsistencyLevel, - serializerSettings, - apitype, - receivedResponseEventArgs, - handler, - sessionContainer, - enableCpuMonitor, - transportClientHandlerFactory, - storeClientFactory) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The cosmos authorization for the client. - /// The event handler to be invoked before the request is sent. - /// The event handler to be invoked after a response has been received. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// Api type for the account - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// The default session container with which DocumentClient is created. - /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. - /// Transport client handler factory. - /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. - /// Flag to allow Quorum Read with Eventual Consistency Account - /// - /// This delegate responsible for validating the third party certificate. - /// This is distributed tracing flag - /// This is the chaos interceptor used for fault injection - /// A boolean flag indicating if stack trace optimization is enabled. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - internal DocumentClient(Uri serviceEndpoint, - AuthorizationTokenProvider cosmosAuthorization, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null, - bool isLocalQuorumConsistency = false, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - IChaosInterceptorFactory chaosInterceptorFactory = null, - bool enableAsyncCacheExceptionNoSharing = true) - { - if (sendingRequestEventArgs != null) - { - this.sendingRequest += sendingRequestEventArgs; - } - - if (serializerSettings != null) - { - this.serializerSettings = serializerSettings; - } - - this.ApiType = apitype; - - if (receivedResponseEventArgs != null) - { - this.receivedResponse += receivedResponseEventArgs; - } - - this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; - this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); - this.transportClientHandlerFactory = transportClientHandlerFactory; - this.IsLocalQuorumConsistency = isLocalQuorumConsistency; - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.chaosInterceptorFactory = chaosInterceptorFactory; - this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); - this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - - this.Initialize( - serviceEndpoint: serviceEndpoint, - connectionPolicy: connectionPolicy, - desiredConsistencyLevel: desiredConsistencyLevel, - handler: handler, - sessionContainer: sessionContainer, - enableCpuMonitor: enableCpuMonitor, - storeClientFactory: storeClientFactory, - cosmosClientId: cosmosClientId, - remoteCertificateValidationCallback: remoteCertificateValidationCallback, - cosmosClientTelemetryOptions: cosmosClientTelemetryOptions, - enableThinClientMode: this.isThinClientEnabled); - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The connection policy for the client. - /// The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Internal constructor purely for unit-testing - /// - internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) - { - // do nothing - this.ServiceEndpoint = serviceEndpoint; - this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); - } - - internal virtual async Task GetCollectionCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.collectionCache; - } - } - - internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.partitionKeyRangeCache; - } - } - - internal GlobalAddressResolver AddressResolver { get; private set; } - - internal GlobalEndpointManager GlobalEndpointManager { get; private set; } - - internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } - - /// - /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. - /// - /// - /// A object. - /// - /// - /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. - /// If there are any initialization exceptions, this method will throw them (set on the task). - /// Alternately, calling any API will throw initialization exception at the first call. - /// - /// - /// - /// - /// - /// - public Task OpenAsync(CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); - } - - private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); - } - - private async Task OpenPrivateAsync(CancellationToken cancellationToken) - { - // Initialize caches for all databases and collections - ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( - new FeedOptions { MaxItemCount = -1 }); - - try - { - while (databaseFeedReader.HasMoreResults) - { - foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) - { - ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( - database.SelfLink, - new FeedOptions { MaxItemCount = -1 }); - List tasks = new List(); - while (collectionFeedReader.HasMoreResults) - { - tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); - } - - await Task.WhenAll(tasks); - } - } - } - catch (DocumentClientException ex) - { - // Clear the caches to ensure that we don't have partial results - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - - DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); - } - } - - internal virtual void Initialize(Uri serviceEndpoint, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - IStoreClientFactory storeClientFactory = null, - TokenCredential tokenCredential = null, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - bool enableThinClientMode = false) - { - if (serviceEndpoint == null) - { - throw new ArgumentNullException("serviceEndpoint"); - } - - this.clientId = cosmosClientId; - this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; - this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); - - this.queryPartitionProvider = new AsyncLazy(async () => - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); - }, CancellationToken.None); - -#if !(NETSTANDARD15 || NETSTANDARD16) -#if NETSTANDARD20 - // GetEntryAssembly returns null when loaded from native netstandard2.0 - if (System.Reflection.Assembly.GetEntryAssembly() != null) - { -#endif - // For tests we want to allow stronger consistency during construction or per call - string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; - if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) - { - if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) - { - this.allowOverrideStrongerConsistency = false; - } - } - - // We might want to override the defaults sometime - string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; - if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) - { - int maxConcurrentConnectionOpenRequestOverrideInt = 0; - if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) - { - this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; - } - } - - string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) - { - int openConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) - { - this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; - } - } - - string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) - { - int idleConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) - { - this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; - } - } - - string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; - if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) - { - int timerPoolGranularityInSecondsOverrideInt = 0; - if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) - { - // timeoutgranularity specified should be greater than min(5 seconds) - if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) - { - this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; - } - } - } - - string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; - if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) - { - bool enableRntbdChannel = false; - if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) - { - this.enableRntbdChannel = enableRntbdChannel; - } - } - - string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; - if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) - { - int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; - if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) - { - this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; - } - } - - string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; - if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) - { - int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; - if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) - { - this.rntbdPartitionCount = rntbdPartitionCount; - } - } - - string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; - if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) - { - int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) - { - this.maxRntbdChannels = maxRntbdChannels; - } - } - - string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; - if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) - { - PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; - if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) - { - this.rntbdPortReuseMode = portReuseMode; - } - } - - string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; - if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) - { - int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; - if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) - { - this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; - } - } - - string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; - if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) - { - int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; - if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) - { - this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; - } - } - - string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) - { - int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) - { - this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; - } - } - - string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) - { - int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) - { - this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; - } - } - - if (enableCpuMonitor.HasValue) - { - this.enableCpuMonitor = enableCpuMonitor.Value; - } - else - { - string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; - if (!string.IsNullOrEmpty(enableCpuMonitorString)) - { - bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; - if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) - { - this.enableCpuMonitor = enableCpuMonitorFlag; - } - } - } -#if NETSTANDARD20 - } -#endif -#endif - - string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); - if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) - { - if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) - { - if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) - { - throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); - } - - this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; - } - } - - // ConnectionPolicy always overrides appconfig - if (connectionPolicy != null) - { - if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) - { - this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) - { - this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) - { - this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; - } - - if (connectionPolicy.MaxTcpPartitionCount.HasValue) - { - this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; - } - - if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) - { - this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; - } - - if (connectionPolicy.PortReuseMode.HasValue) - { - this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; - } - } - - this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); - - this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; - -#if !NETSTANDARD16 - if (ServicePointAccessor.IsSupported) - { - ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); - servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; - } -#endif - this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); - - this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( - this.ApiType, - DocumentClientEventSource.Instance, - this.ConnectionPolicy, - handler, - this.sendingRequest, - this.receivedResponse, - this.chaosInterceptor); - - // Loading VM Information (non blocking call and initialization won't fail if this call fails) - VmMetadataApiHandler.TryInitialize(this.httpClient); - - if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) - { - CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); - CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); - - CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); - } - - // Starting ClientTelemetry Job - this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, - this.ConnectionPolicy, - this.cosmosAuthorization, - this.httpClient, - this.ServiceEndpoint, - this.GlobalEndpointManager, - this.cancellationTokenSource, - this.chaosInterceptor is not null); - - if (sessionContainer != null) - { - this.sessionContainer = sessionContainer; - } - else - { - this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); - } - - this.desiredConsistencyLevel = desiredConsistencyLevel; - // Setup the proxy to be used based on connection mode. - // For gateway: GatewayProxy. - // For direct: WFStoreProxy [set in OpenAsync()]. - this.eventSource = DocumentClientEventSource.Instance; - - this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( - () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), - new ResourceThrottleRetryPolicy( - this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, - this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); - - // Create the task to start the initialize task - // Task will be awaited on in the EnsureValidClientAsync - Task initTask = this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - - // ContinueWith on the initialization task is needed for handling the UnobservedTaskException - // if this task throws for some reason. Awaiting inside a constructor is not supported and - // even if we had to await inside GetInitializationTask to catch the exception, that will - // be a blocking call. In such cases, the recommended approach is to "handle" the - // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted - // and accessing the Exception property on the target task. -#pragma warning disable VSTHRD110 // Observe result of async calls -#pragma warning disable CDX1000 // DontConvertExceptionToObject - initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); -#pragma warning restore CDX1000 // DontConvertExceptionToObject -#pragma warning restore VSTHRD110 // Observe result of async calls - - this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); - DefaultTrace.TraceInformation(string.Format( - CultureInfo.InvariantCulture, - "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", - this.traceId, - serviceEndpoint.ToString(), - this.ConnectionPolicy.ConnectionMode.ToString(), - this.ConnectionPolicy.ConnectionProtocol.ToString(), - desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); - - this.QueryCompatibilityMode = QueryCompatibilityMode.Default; - } - - // Always called from under the lock except when called from Intilialize method during construction. - private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) - { - await this.InitializeGatewayConfigurationReaderAsync(); - - if (this.desiredConsistencyLevel.HasValue) - { - this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); + /// Returns to which the request must be sent + internal IStoreModel GetStoreProxy(DocumentServiceRequest request) + { + // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) + // we return the Gateway store model + if (request.UseGatewayMode) + { + return this.GatewayStoreModel; } - if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride - && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) + ResourceType resourceType = request.ResourceType; + OperationType operationType = request.OperationType; + + if (resourceType == ResourceType.Offer || + (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || + resourceType == ResourceType.PartitionKeyRange || + resourceType == ResourceType.Snapshot || + resourceType == ResourceType.ClientEncryptionKey || + (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) { - this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; + return this.GatewayStoreModel; } - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; - this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); - this.InitializePartitionLevelFailoverWithDefaultHedging(); + if (this.isThinClientEnabled + && operationType == OperationType.Read + && resourceType == ResourceType.Database) + { + return this.GatewayStoreModel; + } - this.PartitionKeyRangeLocation = - this.ConnectionPolicy.EnablePartitionLevelFailover - || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker - ? new GlobalPartitionEndpointManagerCore( - this.GlobalEndpointManager, - this.ConnectionPolicy.EnablePartitionLevelFailover, - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) - : GlobalPartitionEndpointManagerNoOp.Instance; + if (operationType == OperationType.Create + || operationType == OperationType.Upsert) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection || + resourceType == ResourceType.Permission) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Delete) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Read) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else + { + return this.StoreModel; + } + } - this.retryPolicy = new RetryPolicy( - globalEndpointManager: this.GlobalEndpointManager, - connectionPolicy: this.ConnectionPolicy, - partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation); - - this.ResetSessionTokenRetryPolicy = this.retryPolicy; - - GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( - this.GlobalEndpointManager, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); - - this.GatewayStoreModel = gatewayStoreModel; - - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); - - gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + /// + /// The preferred link used in replace operation in SDK. + /// + private string GetLinkForRouting(Documents.Resource resource) + { + // we currently prefer the selflink + return resource.SelfLink ?? resource.AltLink; + } - if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway && this.isThinClientEnabled) - { - ThinClientStoreModel thinClientStoreModel = new ( - endpointManager: this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.chaosInterceptor); - - thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - this.StoreModel = thinClientStoreModel; - } - else if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) - { - this.StoreModel = this.GatewayStoreModel; - } - else - { - this.InitializeDirectConnectivity(storeClientFactory); - } - - return true; - } - - private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) - { - if (databaseName == null) - { - throw new ArgumentNullException(nameof(databaseName)); - } - - if (collection == null) - { - throw new ArgumentNullException(nameof(collection)); - } - - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - using ( - DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Query, - ResourceType.Document, - collection.SelfLink, - AuthorizationTokenType.PrimaryMasterKey)) - { - ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( - resolvedCollection.ResourceId, - new Range( - PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, - PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, - true, - false), - NoOpTrace.Singleton); - - // In Gateway mode, AddressCache is null - if (this.AddressResolver != null) - { - await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); - } - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. - /// - /// - /// - /// The session object used for version tracking when the consistency level is set to Session. - /// - /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. - /// - public object Session - { - get - { - return this.sessionContainer; - } - - set - { - SessionContainer container = value as SessionContainer; - if (container == null) - { - throw new ArgumentNullException("value"); - } - - if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - ClientResources.BadSession, - container.HostName, - this.ServiceEndpoint.Host)); - } - - SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; - if (currentSessionContainer == null) - { - throw new ArgumentNullException(nameof(currentSessionContainer)); - } - - currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. - /// - /// Collection for which session token must be retrieved. - /// - /// The session token used for version tracking when the consistency level is set to Session. - /// - /// - /// The session token can be saved and supplied to a request via . - /// - internal string GetSessionToken(string collectionLink) - { - SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; - - if (sessionContainerInternal == null) - { - throw new ArgumentNullException(nameof(sessionContainerInternal)); - } - - return sessionContainerInternal.GetSessionToken(collectionLink); - } - - /// - /// Gets the Api type - /// - internal ApiType ApiType - { - get; private set; - } - - internal bool UseMultipleWriteLocations { get; private set; } - - /// - /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. - /// - /// - /// The Uri for the service endpoint. - /// - /// - public Uri ServiceEndpoint - { - get; - private set; - } - - /// - /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri WriteEndpoint - { - get - { - return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri ReadEndpoint - { - get - { - return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the Connection policy used by the client from the Azure Cosmos DB service. - /// - /// - /// The Connection policy used by the client. - /// - /// - public ConnectionPolicy ConnectionPolicy { get; private set; } - - /// - /// Gets the AuthKey used by the client from the Azure Cosmos DB service. - /// - /// - /// The AuthKey used by the client. - /// - /// - public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); - - /// - /// Gets the configured consistency level of the client from the Azure Cosmos DB service. - /// - /// - /// The configured of the client. - /// - /// - public virtual Documents.ConsistencyLevel ConsistencyLevel - { - get - { -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); -#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits - return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : - this.accountServiceConfiguration.DefaultConsistencyLevel; - } - } - - /// - /// Returns the account properties available in the service configuration if the client was initialized. - /// - public bool TryGetCachedAccountProperties(out AccountProperties properties) - { - if (this.isSuccessfullyInitialized - && this.accountServiceConfiguration != null - && this.accountServiceConfiguration.AccountProperties != null) - { - properties = this.accountServiceConfiguration.AccountProperties; - return true; - } - - properties = null; - return false; - } - - /// - /// Disposes the client for the Azure Cosmos DB service. - /// - /// - /// - /// - /// - /// - public void Dispose() - { - if (this.isDisposed) - { - return; - } - - if (this.telemetryToServiceHelper != null) - { - this.telemetryToServiceHelper.Dispose(); - this.telemetryToServiceHelper = null; - } - - if (!this.cancellationTokenSource.IsCancellationRequested) - { - this.cancellationTokenSource.Cancel(); - } - - this.cancellationTokenSource.Dispose(); - - if (this.StoreModel != null) - { - this.StoreModel.Dispose(); - this.StoreModel = null; - } - - if (this.storeClientFactory != null) - { - // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference - if (this.isStoreClientFactoryCreatedInternally) - { - this.storeClientFactory.Dispose(); - } - - this.storeClientFactory = null; - } - - if (this.AddressResolver != null) - { - this.AddressResolver.Dispose(); - this.AddressResolver = null; - } - - if (this.httpClient != null) - { - try - { - this.httpClient.Dispose(); - } - catch (Exception exception) - { - DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", - exception.Message); - } - - this.httpClient = null; - } - - if (this.cosmosAuthorization != null) - { - this.cosmosAuthorization.Dispose(); - } - - if (this.GlobalEndpointManager != null) - { - this.GlobalEndpointManager.Dispose(); - this.GlobalEndpointManager = null; - } - - if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) - { - this.queryPartitionProvider.Value.Dispose(); - } - - if (this.initTaskCache != null) - { - this.initTaskCache.Dispose(); - this.initTaskCache = null; - } - - DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); - DefaultTrace.Flush(); - - this.isDisposed = true; - } - - //Compatibility mode: - // Allows to specify compatibility mode used by client when making query requests. - // should be removed when application/sql is no longer supported. - internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } - - /// - /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). - /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache - /// to resolves the request's collection name. If it differs from the session container's resource id it - /// explains the session unavailable exception: somebody removed and recreated the collection. In this - /// case we retry once again (with empty session token) otherwise we return the error to the client - /// (see RenameCollectionAwareClientRetryPolicy) - /// - internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } - - /// - /// Gets and sets the IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension StoreModel { get; set; } - - /// - /// Gets and sets the gateway IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension GatewayStoreModel { get; set; } - - /// - /// Gets and sets on execute scalar query callback - /// - /// - /// Test hook to enable unit test for scalar queries - /// - internal Action OnExecuteScalarQueryCallback { get; set; } - - internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; - - internal virtual async Task GetDefaultConsistencyLevelAsync() - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; - } - - internal Task GetDesiredConsistencyLevelAsync() - { - return Task.FromResult(this.desiredConsistencyLevel); - } - - internal async Task ProcessRequestAsync( - string verb, - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - string testAuthorization = null) // Only for unit-tests - { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (verb == null) - { - throw new ArgumentNullException(nameof(verb)); - } - - (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( - request.ResourceAddress, - PathsHelper.GetResourcePath(request.ResourceType), - verb, - request.Headers, - AuthorizationTokenType.PrimaryMasterKey); - - // Unit-test hook - if (testAuthorization != null) - { - payload = testAuthorization; - authorization = testAuthorization; - } - request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; - - try - { - return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); - } - catch (DocumentClientException dce) - { - this.cosmosAuthorization.TraceUnauthorized( - dce, - authorization, - payload); - - throw; - } - } - - internal Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); - } - - internal async Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - ITrace trace, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(trace); - - retryPolicyInstance?.OnBeforeSendRequest(request); - - using (new ActivityScope(Guid.NewGuid())) - { - IStoreModel storeProxy = this.GetStoreProxy(request); - return await storeProxy.ProcessMessageAsync(request, cancellationToken); - } - } - - /// - /// Establishes and Initializes the Rntbd connection to all the backend replica nodes - /// for the given database name and container. - /// - /// A string containing the cosmos database name. - /// A string containing the cosmos container link uri. - /// An instance of the . - internal async Task OpenConnectionsToAllReplicasAsync( - string databaseName, - string containerLinkUri, - CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(databaseName) || - string.IsNullOrEmpty(containerLinkUri)) - { - string resourceName = string.IsNullOrEmpty(databaseName) ? - nameof(databaseName) : - nameof(containerLinkUri); - - throw new ArgumentNullException(resourceName); - } - - if (this.StoreModel != null) - { - try - { - await this.StoreModel.OpenConnectionsToAllReplicasAsync( - databaseName, - containerLinkUri, - cancellationToken); - } - catch (Exception) - { - throw; - } - } - } - - private static string NormalizeAuthorizationPayload(string input) - { - const int expansionBuffer = 12; - StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); - for (int i = 0; i < input.Length; i++) - { - switch (input[i]) - { - case '\n': - builder.Append("\\n"); - break; - case '/': - builder.Append("\\/"); - break; - default: - builder.Append(input[i]); - break; - } - } - - return builder.ToString(); - } - - internal async Task InitilizeFaultInjectionAsync() - { - if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) - { - this.isChaosInterceptorInititalized = true; - await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); - } - } - - internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) - { - return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.maxRequestsPerRntbdChannel, - this.maxRntbdChannels, - this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - this.rntbdPortReuseMode); - } - - internal virtual async Task EnsureValidClientAsync(ITrace trace) - { - if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) - { - return; - } - - // Trace when the Initialization of client has not been completed. Usually during first call - using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) - { - // If the initialization task failed, we should retry initialization. - // We may end up throwing the same exception but this will ensure that we dont have a - // client which is unusable and can resume working if it failed initialization once. - // If we have to reinitialize the client, it needs to happen in thread safe manner so that - // we dont re-initalize the task again for each incoming call. - try - { - this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - } - catch (DocumentClientException ex) - { - throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( - dce: ex, - trace: trace); - } - catch (Exception e) - { - DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); - childTrace.AddDatum("initializeTask failed", e.Message); - throw; - } - - await this.InitilizeFaultInjectionAsync(); - } - } - - #region Create Impl - /// - /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed. - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); - } - - private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, - Documents.Client.RequestOptions options) - { - if (database == null) - { - throw new ArgumentNullException("database"); - } - - // Doing a Read before Create will give us better latency for existing databases - try - { - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDatabaseAsync(database, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - - /// - /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to create. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// Finally, a Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Create a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, - object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, - CancellationToken cancellationToken = default) - { - // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy); - } - - private async Task> CreateDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 collection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDocumentCollectionPrivateAsync( - string databaseLink, - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse( - await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 DocumentCollection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); - } - - private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( - string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) - { - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make - // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. - Documents.Database database = await this.ReadDatabaseAsync(databaseLink); - - // Doing a Read before Create will give us better latency for existing collections. - // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, - // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. - try - { - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - - /// - /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link to the source object. - /// The target object. - /// (optional)The point in time to restore. If null, use the latest restorable time. - /// (Optional) The for the request. - /// The task object representing the service response for the asynchronous operation. - internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - if (targetDocumentCollection == null) - { - throw new ArgumentNullException("targetDocumentCollection"); - } - - bool isFeed; - string resourceTypeString; - string resourceIdOrFullName; - bool isNameBased; - - string dbsId; - string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); - if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - dbsId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - string sourceCollId; - if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - sourceCollId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - this.ValidateResource(targetDocumentCollection); - - if (options == null) - { - options = new Documents.Client.RequestOptions(); - } - if (!options.RemoteStorageType.HasValue) - { - options.RemoteStorageType = RemoteStorageType.Standard; - } - options.SourceDatabaseId = dbsId; - options.SourceCollectionId = sourceCollId; - if (restoreTime.HasValue) - { - options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - targetDocumentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Get the status of a collection being restored in the Azure Cosmos DB service. - /// - /// The link of the document collection being restored. - /// The task object representing the service response for the asynchronous operation. - internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (string.IsNullOrEmpty(targetDocumentCollectionLink)) - { - throw new ArgumentNullException("targetDocumentCollectionLink"); - } - - ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( - targetDocumentCollectionLink, - new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, - retryPolicyInstance); - string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); - if (restoreState == null) - { - restoreState = RestoreState.RestoreCompleted.ToString(); - } - - DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() - { - State = restoreState - }; - - return ret; - } - - /// - /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ - /// The object to create. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the created object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. - /// - /// - /// 409 - /// - /// Conflict - This means a with an id matching the id field of already existed, - /// or there is already a pending snapshot for the specified resource link. - /// - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that - /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (snapshot == null) - { - throw new ArgumentNullException("snapshot"); - } - - this.ValidateResource(snapshot); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Snapshots_Root, - snapshot, - ResourceType.Snapshot, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Delete Impl - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. snapshots/snapshot_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Replace Impl - /// - /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. - /// - /// the updated document collection. - /// the request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceDocumentCollectionPrivateAsync( - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(documentCollection), - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - // set the session token - if (collection.Resource != null) - { - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - } - return collection; - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If either or is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now dynamically cast doc back to your MyPoco - /// MyPoco poco = (dynamic)doc; - /// - /// //Update some properties of the poco object - /// poco.MyProperty = "updated value"; - /// - /// //Now persist these changes to the database using doc.SelLink and the update poco object - /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if ((options == null) || (options.PartitionKey == null)) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible( - () => this.ReplaceDocumentPrivateAsync( - documentLink, - document, - options, - requestRetryPolicy, - cancellationToken), - requestRetryPolicy, - cancellationToken); - } - - private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// doc.SetPropertyValue("MyProperty", "updated value"); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Document updated = await client.ReplaceDocumentAsync(doc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( - this.GetLinkForRouting(document), - document, - options, - retryPolicyInstance, - cancellationToken), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - this.ValidateResource(document); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - documentLink, - document, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, document, options); - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "sproc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// sproc.Body = "function () {new javascript body for sproc}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceStoredProcedurePrivateAsync( - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(storedProcedure), - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "trigger id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// trigger.Body = "function () {new javascript body for trigger}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// Trigger updated = await client.ReplaceTriggerAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(trigger), - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "udf id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// udf.Body = "function () {new javascript body for udf}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedFunctionPrivateAsync( - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(function), - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. - /// - /// - /// - /// - /// - /// r.ResourceLink == "collection selfLink") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Create a new offer with the changed throughput - /// OfferV2 newOffer = new OfferV2(offer, 5000); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Offer updated = await client.ReplaceOfferAsync(newOffer); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceOfferAsync(Offer offer) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (offer == null) - { - throw new ArgumentNullException("offer"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - offer.SelfLink, - offer, - ResourceType.Offer, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse( - await this.UpdateAsync(request, retryPolicyInstance), - OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "user defined type id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(userDefinedType), - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Read Impl - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Database resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// (docLink); - /// ]]> - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the DocumentCollection to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionPrivateAsync( - string documentCollectionLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the stored procedure to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Trigger to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the User Defined Function to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Conflict to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads an from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Offer to be read. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. - /// - /// - /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about - /// minimum throughput of a Cosmos container (or a database) - /// To retrieve the minimum throughput for a collection/database, use the following sample - /// - /// response = await client.ReadOfferAsync(offer.SelfLink); - /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; - /// ]]> - /// - /// - /// - /// - /// - /// - /// - /// - public Task> ReadOfferAsync(string offerLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(offerLink)) - { - throw new ArgumentNullException("offerLink"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Offer, - offerLink, - null, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Reads a as an asynchronous operation. - /// - /// The link for the schema to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Schema are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentSchemaLink)) - { - throw new ArgumentNullException("documentSchemaLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Schema, - documentSchemaLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the UserDefinedType resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypeLink)) - { - throw new ArgumentNullException("userDefinedTypeLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedType, - userDefinedTypeLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Snapshot resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Snapshot are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/snapshots/{snapshot identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region ReadFeed Impl - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDatabaseFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = null; - /// List ids = new List(); - /// do - /// { - /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); - /// foreach (var item in response) - /// { - /// ids.Add(item.Id); - /// } - /// } - /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(partitionKeyRangesLink)) - { - throw new ArgumentNullException("partitionKeyRangesLink"); - } - - return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionsLink)) - { - throw new ArgumentNullException("collectionsLink"); - } - - return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProceduresLink)) - { - throw new ArgumentNullException("storedProceduresLink"); - } - - return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggersLink)) - { - throw new ArgumentNullException("triggersLink"); - } - - return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedFunctionsLink)) - { - throw new ArgumentNullException("userDefinedFunctionsLink"); - } - - return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. - /// This takes returns a which will contain an enumerable list of dynamic objects. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a containing dynamic objects representing the items in the feed. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. - /// This is important becuse a DocumentCollection can contain different kinds of documents. - /// - /// - /// - /// - public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentsLink)) - { - throw new ArgumentNullException("documentsLink"); - } - - DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); - return new DocumentFeedResponse( - response.Cast(), - response.Count, - response.Headers, - response.UseETagAsContinuation, - response.QueryMetrics, - response.RequestStatistics, - responseLengthBytes: response.ResponseLengthBytes); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); - } - - private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictsLink)) - { - throw new ArgumentNullException("conflictsLink"); - } - - return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service - /// as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadOfferAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadOffersFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionSchemaLink)) - { - throw new ArgumentNullException("documentCollectionSchemaLink"); - } - - return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypesLink)) - { - throw new ArgumentNullException("userDefinedTypesLink"); - } - - return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a set of containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadSnapshotFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); - } - - #endregion - - #region Stored procs - /// - /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) - { - return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - default, - procedureParams), - retryPolicyInstance); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - cancellationToken, - procedureParams), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ExecuteStoredProcedurePrivateAsync( - string storedProcedureLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - params dynamic[] procedureParams) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); - string storedProcedureInput = serializerSettings == null ? - JsonConvert.SerializeObject(procedureParams) : - JsonConvert.SerializeObject(procedureParams, serializerSettings); - using (MemoryStream storedProcedureInputStream = new MemoryStream()) - { - using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) - { - await writer.WriteAsync(storedProcedureInput); - await writer.FlushAsync(); - storedProcedureInputStream.Position = 0; - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.ExecuteJavaScript, - ResourceType.StoredProcedure, - storedProcedureLink, - storedProcedureInputStream, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); - if (options?.PartitionKeyRangeId == null) - { - await this.AddPartitionKeyInformationAsync( - request, - options); - } - - retryPolicyInstance?.OnBeforeSendRequest(request); - - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new StoredProcedureResponse(await this.ExecuteProcedureAsync( - request, - retryPolicyInstance, - cancellationToken), - this.GetSerializerSettingsForRequest(options)); - } - } - } - } - - #endregion - - #region Upsert Impl - /// - /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to upsert. - /// (Optional) The for the request. - /// The that was upserted within a task object representing the service response for the asynchronous operation. - /// If is not set - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// - /// - /// The example below upserts a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to upsert. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// A Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Upsert a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) - { - // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy, cancellationToken); - } - - private async Task> UpsertDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - // To be implemented. - throw new NotImplementedException(); - } - - /// - /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. - /// - /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ - /// The object to upsert. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the upserted object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - #endregion - - #region IAuthorizationTokenProvider - - ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType) - { - return this.cosmosAuthorization.GetUserAuthorizationAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType); - } - - ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType, - ITrace trace) - { - return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType, - trace); - } - - Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( - DocumentServiceRequest request, - string federationId, - string verb, - string resourceId) - { - return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( - request, - federationId, - verb, - resourceId); - } - - #endregion - - #region Core Implementation - internal Task CreateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpdateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); - } - - internal Task ReadAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task ReadFeedAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task DeleteAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteProcedureAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteQueryAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpsertAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - #endregion - - /// - /// Read the from the Azure Cosmos DB service as an asynchronous operation. - /// - /// - /// A wrapped in a object. - /// - public Task GetDatabaseAccountAsync() - { - return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); - } - - /// - /// Read the as an asynchronous operation - /// given a specific reginal endpoint url. - /// - /// The reginal url of the serice endpoint. - /// The CancellationToken - /// - /// A wrapped in a object. - /// - Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) - { - return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); - } - - private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) - { - async ValueTask CreateRequestMessage() - { - HttpRequestMessage request = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = serviceEndpoint - }; - - INameValueCollection headersCollection = new StoreResponseNameValueCollection(); - await this.cosmosAuthorization.AddAuthorizationHeaderAsync( - headersCollection, - serviceEndpoint, - "GET", - AuthorizationTokenType.PrimaryMasterKey); - - foreach (string key in headersCollection.AllKeys()) - { - request.Headers.Add(key, headersCollection[key]); - } - - return request; - } - - AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, - clientSideRequestStatistics: null); - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; - - if (this.queryPartitionProvider.IsValueCreated) - { - (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); - } - - return databaseAccount; - } - - return null; - } - - #region Private Impl - - /// - /// Certain requests must be routed through gateway even when the client connectivity mode is direct. - /// For e.g., DocumentCollection creation. This method returns the based - /// on the input . - /// - /// Returns to which the request must be sent - internal IStoreModel GetStoreProxy(DocumentServiceRequest request) - { - // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) - // we return the Gateway store model - if (request.UseGatewayMode) - { - return this.GatewayStoreModel; - } - - ResourceType resourceType = request.ResourceType; - OperationType operationType = request.OperationType; - - if (resourceType == ResourceType.Offer || - (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || - resourceType == ResourceType.PartitionKeyRange || - resourceType == ResourceType.Snapshot || - resourceType == ResourceType.ClientEncryptionKey || - (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) - { - return this.GatewayStoreModel; - } - - if (this.isThinClientEnabled - && operationType == OperationType.Read - && resourceType == ResourceType.Database) - { - return this.GatewayStoreModel; - } - - if (operationType == OperationType.Create - || operationType == OperationType.Upsert) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection || - resourceType == ResourceType.Permission) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Delete) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Read) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else - { - return this.StoreModel; - } - } - - /// - /// The preferred link used in replace operation in SDK. - /// - private string GetLinkForRouting(Documents.Resource resource) - { - // we currently prefer the selflink - return resource.SelfLink ?? resource.AltLink; - } - - internal void EnsureValidOverwrite( - Documents.ConsistencyLevel desiredConsistencyLevel, - OperationType? operationType = null, - ResourceType? resourceType = null) - { - Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; - if (!this.IsValidConsistency( - defaultConsistencyLevel, - desiredConsistencyLevel, - operationType, - resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - desiredConsistencyLevel.ToString(), - defaultConsistencyLevel.ToString())); - } - } - - private bool IsValidConsistency( - Documents.ConsistencyLevel backendConsistency, - Documents.ConsistencyLevel desiredConsistency, - OperationType? operationType, - ResourceType? resourceType) - { - if (this.allowOverrideStrongerConsistency) - { - return true; - } - - return ValidationHelpers.IsValidConsistencyLevelOverwrite( - backendConsistency: backendConsistency, - desiredConsistency: desiredConsistency, - isLocalQuorumConsistency: this.IsLocalQuorumConsistency, - operationType: operationType, - resourceType: resourceType); - } - - private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) - { - // Check if we have a store client factory in input and if we do, do not initialize another store client - // The purpose is to reuse store client factory across all document clients inside compute gateway - if (storeClientFactory != null) - { - this.storeClientFactory = storeClientFactory; - this.isStoreClientFactoryCreatedInternally = false; - } - else - { - // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, - // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. - Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () - { -#if INTERNAL - IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing -#else - IsDistributedTracingEnabled = false -#endif - - }; - - StoreClientFactory newClientFactory = new StoreClientFactory( - this.ConnectionPolicy.ConnectionProtocol, - (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, - this.maxConcurrentConnectionOpenRequests, - this.ConnectionPolicy.UserAgentContainer, - this.eventSource, - null, - this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.timerPoolGranularityInSeconds, - this.maxRntbdChannels, - this.rntbdPartitionCount, - this.maxRequestsPerRntbdChannel, - (Documents.PortReuseMode)this.rntbdPortReuseMode, - this.rntbdPortPoolReuseThreshold, - this.rntbdPortPoolBindAttempts, - receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, - sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, - retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), - enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, - remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, - distributedTracingOptions: distributedTracingOptions, - enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), - chaosInterceptor: this.chaosInterceptor); - - if (this.transportClientHandlerFactory != null) - { - newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); - } - - this.storeClientFactory = newClientFactory; - this.isStoreClientFactoryCreatedInternally = true; - } - - this.AddressResolver = new GlobalAddressResolver( - this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.ConnectionPolicy.ConnectionProtocol, - this, - this.collectionCache, - this.partitionKeyRangeCache, - this.accountServiceConfiguration, - this.ConnectionPolicy, - this.httpClient, - this.storeClientFactory.GetConnectionStateListener(), - this.enableAsyncCacheExceptionNoSharing); - - this.CreateStoreModel(subscribeRntbdStatus: true); - } - - private void CreateStoreModel(bool subscribeRntbdStatus) - { - //EnableReadRequestsFallback, if not explicity set on the connection policy, - //is false if the account's consistency is bounded staleness, - //and true otherwise. - StoreClient storeClient = this.storeClientFactory.CreateStoreClient( - this.AddressResolver, - this.sessionContainer, - this.accountServiceConfiguration, - this, - true, - this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), - !this.enableRntbdChannel, - this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), - true, + internal void EnsureValidOverwrite( + Documents.ConsistencyLevel desiredConsistencyLevel, + OperationType? operationType = null, + ResourceType? resourceType = null) + { + Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; + if (!this.IsValidConsistency( + defaultConsistencyLevel, + desiredConsistencyLevel, + operationType, + resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + desiredConsistencyLevel.ToString(), + defaultConsistencyLevel.ToString())); + } + } + + private bool IsValidConsistency( + Documents.ConsistencyLevel backendConsistency, + Documents.ConsistencyLevel desiredConsistency, + OperationType? operationType, + ResourceType? resourceType) + { + if (this.allowOverrideStrongerConsistency) + { + return true; + } + + return ValidationHelpers.IsValidConsistencyLevelOverwrite( + backendConsistency: backendConsistency, + desiredConsistency: desiredConsistency, + isLocalQuorumConsistency: this.IsLocalQuorumConsistency, + operationType: operationType, + resourceType: resourceType); + } + + private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) + { + // Check if we have a store client factory in input and if we do, do not initialize another store client + // The purpose is to reuse store client factory across all document clients inside compute gateway + if (storeClientFactory != null) + { + this.storeClientFactory = storeClientFactory; + this.isStoreClientFactoryCreatedInternally = false; + } + else + { + // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, + // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. + Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () + { +#if INTERNAL + IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing +#else + IsDistributedTracingEnabled = false +#endif + + }; + + StoreClientFactory newClientFactory = new StoreClientFactory( + this.ConnectionPolicy.ConnectionProtocol, + (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, + this.maxConcurrentConnectionOpenRequests, + this.ConnectionPolicy.UserAgentContainer, + this.eventSource, + null, + this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.timerPoolGranularityInSeconds, + this.maxRntbdChannels, + this.rntbdPartitionCount, + this.maxRequestsPerRntbdChannel, + (Documents.PortReuseMode)this.rntbdPortReuseMode, + this.rntbdPortPoolReuseThreshold, + this.rntbdPortPoolBindAttempts, + receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, + sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, + retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), + enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, + remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, + distributedTracingOptions: distributedTracingOptions, + enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), + chaosInterceptor: this.chaosInterceptor); + + if (this.transportClientHandlerFactory != null) + { + newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); + } + + this.storeClientFactory = newClientFactory; + this.isStoreClientFactoryCreatedInternally = true; + } + + this.AddressResolver = new GlobalAddressResolver( + this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.ConnectionPolicy.ConnectionProtocol, + this, + this.collectionCache, + this.partitionKeyRangeCache, + this.accountServiceConfiguration, + this.ConnectionPolicy, + this.httpClient, + this.storeClientFactory.GetConnectionStateListener(), + this.enableAsyncCacheExceptionNoSharing); + + this.CreateStoreModel(subscribeRntbdStatus: true); + } + + private void CreateStoreModel(bool subscribeRntbdStatus) + { + //EnableReadRequestsFallback, if not explicity set on the connection policy, + //is false if the account's consistency is bounded staleness, + //and true otherwise. + StoreClient storeClient = this.storeClientFactory.CreateStoreClient( + this.AddressResolver, + this.sessionContainer, + this.accountServiceConfiguration, + this, + true, + this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), + !this.enableRntbdChannel, + this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), + true, enableReplicaValidation: this.isReplicaAddressValidationEnabled, - sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); - - if (subscribeRntbdStatus) - { - storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); - } - - storeClient.SerializerSettings = this.serializerSettings; - - this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); - } - - private void DisableRntbdChannel() - { - Debug.Assert(this.enableRntbdChannel); - this.enableRntbdChannel = false; - this.CreateStoreModel(subscribeRntbdStatus: false); - } - - private async Task InitializeGatewayConfigurationReaderAsync() - { - GatewayAccountReader accountReader = new GatewayAccountReader( - serviceEndpoint: this.ServiceEndpoint, - cosmosAuthorization: this.cosmosAuthorization, - connectionPolicy: this.ConnectionPolicy, - httpClient: this.httpClient, - cancellationToken: this.cancellationTokenSource.Token, - isThinClientEnabled: this.isThinClientEnabled); - - this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); - + sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); + + if (subscribeRntbdStatus) + { + storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); + } + + storeClient.SerializerSettings = this.serializerSettings; + + this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); + } + + private void DisableRntbdChannel() + { + Debug.Assert(this.enableRntbdChannel); + this.enableRntbdChannel = false; + this.CreateStoreModel(subscribeRntbdStatus: false); + } + + private async Task InitializeGatewayConfigurationReaderAsync() + { + GatewayAccountReader accountReader = new GatewayAccountReader( + serviceEndpoint: this.ServiceEndpoint, + cosmosAuthorization: this.cosmosAuthorization, + connectionPolicy: this.ConnectionPolicy, + httpClient: this.httpClient, + cancellationToken: this.cancellationTokenSource.Token, + isThinClientEnabled: this.isThinClientEnabled); + + this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); + await this.accountServiceConfiguration.InitializeAsync(); AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; - this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); - } + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; + this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); + } internal string GetUserAgentFeatures() { @@ -6887,379 +6888,379 @@ internal void InitializePartitionLevelFailoverWithDefaultHedging() thresholdStep: TimeSpan.FromMilliseconds(DocumentClient.DefaultHedgingThresholdStepInMilliseconds)); } } - - internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) - { - this.sessionContainer.SetSessionToken(request, response.Headers); - } - - internal DocumentServiceRequest CreateDocumentServiceRequest( - OperationType operationType, - string resourceLink, - ResourceType resourceType, - INameValueCollection headers) - { - if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) - { - return DocumentServiceRequest.Create( - operationType, - null, - resourceType, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - else - { - return DocumentServiceRequest.Create( - operationType, - resourceType, - resourceLink, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - } - - internal void ValidateResource(Documents.Resource resource) - { - this.ValidateResource(resource.Id); - } - - internal void ValidateResource(string resourceId) - { - if (!string.IsNullOrEmpty(resourceId)) - { - int match = resourceId.IndexOfAny(resourceIdSeparators); - if (match != -1) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidCharacterInResourceName, - resourceId[match])); - } - - if (resourceId[resourceId.Length - 1] == ' ') - { - throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); - } - } - } - - private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - PartitionKeyInternal partitionKey; - if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else if (options?.PartitionKey != null) - { - partitionKey = options.PartitionKey.InternalKey; - } - else - { - partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - // For backward compatibility, if collection doesn't have partition key defined, we assume all documents - // have empty value for it and user doesn't need to specify it explicitly. - PartitionKeyInternal partitionKey; - if (options?.PartitionKey == null) - { - if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) - { - partitionKey = PartitionKeyInternal.Empty; - } - else - { - throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); - } - } - else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else - { - partitionKey = options.PartitionKey.InternalKey; - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) - { - return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; - } - - private INameValueCollection GetRequestHeaders( - Documents.Client.RequestOptions options, - OperationType operationType, - ResourceType resourceType) - { - Debug.Assert( - this.isSuccessfullyInitialized, - "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); - - RequestNameValueCollection headers = new RequestNameValueCollection(); - - if (this.UseMultipleWriteLocations) - { - headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); - } - - if (this.desiredConsistencyLevel.HasValue) - { - // check anyways since default consistency level might have been refreshed. - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: this.desiredConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); - } - - if (options == null) - { - return headers; - } - - if (options.AccessCondition != null) - { - if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) - { - headers.IfMatch = options.AccessCondition.Condition; - } - else - { - headers.IfNoneMatch = options.AccessCondition.Condition; - } - } - - if (options.ConsistencyLevel.HasValue) - { - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: options.ConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); - } - - if (options.PriorityLevel.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); - } - - if (options.IndexingDirective.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); - } - - if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) - { - string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); - } - - if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) - { - string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); - } - - if (!string.IsNullOrEmpty(options.SessionToken)) - { - headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; - } - - if (options.ResourceTokenExpirySeconds.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferType != null) - { - headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); - } - - if (options.OfferThroughput.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferEnableRUPerMinuteThroughput) - { - headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); - } - - if (options.InsertSystemPartitionKey) - { - headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); - } - - //if (options.OfferAutopilotTier.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); - //} - - //if (options.OfferAutopilotAutoUpgrade.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); - //} - - if (options.EnableScriptLogging) - { - headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); - } - - if (options.PopulateQuotaInfo) - { - headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); - } - - if (options.PopulateRestoreStatus) - { - headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); - } - - if (options.PopulatePartitionKeyRangeStatistics) - { - headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); - } - - if (options.DisableRUPerMinuteUsage) - { - headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); - } - - if (options.RemoteStorageType.HasValue) - { - headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); - } - - if (options.PartitionKeyRangeId != null) - { - headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); - } - - if (options.SourceDatabaseId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); - } - - if (options.SourceCollectionId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); - } - - if (options.RestorePointInTime.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.IsReadOnlyScript) - { - headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); - } - - if (options.IncludeSnapshotDirectories) - { - headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); - } - - if (options.ExcludeSystemProperties.HasValue) - { - headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); - } - - if (options.MergeStaticId != null) - { - headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); - } - - if (options.PreserveFullContent) - { - headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); - } - - if (options.ThroughputBucket.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); - } - - return headers; - } - - private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory - { - private readonly IRetryPolicyFactory retryPolicy; - private readonly ISessionContainer sessionContainer; - private readonly ClientCollectionCache collectionCache; - - public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) - { - this.retryPolicy = retryPolicy; - this.sessionContainer = sessionContainer; - this.collectionCache = collectionCache; - } - - public IDocumentClientRetryPolicy GetRequestPolicy() - { - return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); - } - } - - private class HttpRequestMessageHandler : DelegatingHandler - { - private readonly EventHandler sendingRequest; - private readonly EventHandler receivedResponse; - - public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) - { - this.sendingRequest = sendingRequest; - this.receivedResponse = receivedResponse; - - this.InnerHandler = innerHandler ?? new HttpClientHandler(); - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); - HttpResponseMessage response = await base.SendAsync(request, cancellationToken); - this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); - return response; - } - } - - #endregion - } -} + + internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) + { + this.sessionContainer.SetSessionToken(request, response.Headers); + } + + internal DocumentServiceRequest CreateDocumentServiceRequest( + OperationType operationType, + string resourceLink, + ResourceType resourceType, + INameValueCollection headers) + { + if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) + { + return DocumentServiceRequest.Create( + operationType, + null, + resourceType, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + else + { + return DocumentServiceRequest.Create( + operationType, + resourceType, + resourceLink, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + } + + internal void ValidateResource(Documents.Resource resource) + { + this.ValidateResource(resource.Id); + } + + internal void ValidateResource(string resourceId) + { + if (!string.IsNullOrEmpty(resourceId)) + { + int match = resourceId.IndexOfAny(resourceIdSeparators); + if (match != -1) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidCharacterInResourceName, + resourceId[match])); + } + + if (resourceId[resourceId.Length - 1] == ' ') + { + throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); + } + } + } + + private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + PartitionKeyInternal partitionKey; + if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else if (options?.PartitionKey != null) + { + partitionKey = options.PartitionKey.InternalKey; + } + else + { + partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + // For backward compatibility, if collection doesn't have partition key defined, we assume all documents + // have empty value for it and user doesn't need to specify it explicitly. + PartitionKeyInternal partitionKey; + if (options?.PartitionKey == null) + { + if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) + { + partitionKey = PartitionKeyInternal.Empty; + } + else + { + throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); + } + } + else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else + { + partitionKey = options.PartitionKey.InternalKey; + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) + { + return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; + } + + private INameValueCollection GetRequestHeaders( + Documents.Client.RequestOptions options, + OperationType operationType, + ResourceType resourceType) + { + Debug.Assert( + this.isSuccessfullyInitialized, + "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); + + RequestNameValueCollection headers = new RequestNameValueCollection(); + + if (this.UseMultipleWriteLocations) + { + headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); + } + + if (this.desiredConsistencyLevel.HasValue) + { + // check anyways since default consistency level might have been refreshed. + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: this.desiredConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); + } + + if (options == null) + { + return headers; + } + + if (options.AccessCondition != null) + { + if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) + { + headers.IfMatch = options.AccessCondition.Condition; + } + else + { + headers.IfNoneMatch = options.AccessCondition.Condition; + } + } + + if (options.ConsistencyLevel.HasValue) + { + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: options.ConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); + } + + if (options.PriorityLevel.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); + } + + if (options.IndexingDirective.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); + } + + if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) + { + string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); + } + + if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) + { + string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); + } + + if (!string.IsNullOrEmpty(options.SessionToken)) + { + headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; + } + + if (options.ResourceTokenExpirySeconds.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferType != null) + { + headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); + } + + if (options.OfferThroughput.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferEnableRUPerMinuteThroughput) + { + headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); + } + + if (options.InsertSystemPartitionKey) + { + headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); + } + + //if (options.OfferAutopilotTier.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); + //} + + //if (options.OfferAutopilotAutoUpgrade.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); + //} + + if (options.EnableScriptLogging) + { + headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); + } + + if (options.PopulateQuotaInfo) + { + headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); + } + + if (options.PopulateRestoreStatus) + { + headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); + } + + if (options.PopulatePartitionKeyRangeStatistics) + { + headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); + } + + if (options.DisableRUPerMinuteUsage) + { + headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); + } + + if (options.RemoteStorageType.HasValue) + { + headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); + } + + if (options.PartitionKeyRangeId != null) + { + headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); + } + + if (options.SourceDatabaseId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); + } + + if (options.SourceCollectionId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); + } + + if (options.RestorePointInTime.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.IsReadOnlyScript) + { + headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); + } + + if (options.IncludeSnapshotDirectories) + { + headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); + } + + if (options.ExcludeSystemProperties.HasValue) + { + headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); + } + + if (options.MergeStaticId != null) + { + headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); + } + + if (options.PreserveFullContent) + { + headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); + } + + if (options.ThroughputBucket.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); + } + + return headers; + } + + private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory + { + private readonly IRetryPolicyFactory retryPolicy; + private readonly ISessionContainer sessionContainer; + private readonly ClientCollectionCache collectionCache; + + public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) + { + this.retryPolicy = retryPolicy; + this.sessionContainer = sessionContainer; + this.collectionCache = collectionCache; + } + + public IDocumentClientRetryPolicy GetRequestPolicy() + { + return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); + } + } + + private class HttpRequestMessageHandler : DelegatingHandler + { + private readonly EventHandler sendingRequest; + private readonly EventHandler receivedResponse; + + public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) + { + this.sendingRequest = sendingRequest; + this.receivedResponse = receivedResponse; + + this.InnerHandler = innerHandler ?? new HttpClientHandler(); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); + HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); + return response; + } + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 6a0f33131f..22a79eefc6 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -368,7 +368,7 @@ internal static async Task> TryResolveSessionTokenAsync( return new Tuple(false, null); } - private static async Task> TryResolvePartitionKeyRangeAsync( + protected static async Task> TryResolvePartitionKeyRangeAsync( DocumentServiceRequest request, ISessionContainer sessionContainer, PartitionKeyRangeCache partitionKeyRangeCache, diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs index 4b6c715211..fc134e2693 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs @@ -169,7 +169,9 @@ public override bool TryMarkEndpointUnavailableForPartitionKeyRange( { // For multi master write accounts, since all the regions are treated as write regions, the next locations to fail over // will be the preferred read regions that are configured in the application preferred regions in the CosmosClientOptions. - ReadOnlyCollection nextLocations = this.globalEndpointManager.ReadEndpoints; + ReadOnlyCollection nextLocations = ConfigurationManager.IsThinClientEnabled(defaultValue: false) && ThinClientStoreModel.IsOperationSupportedByThinClient(request) + ? this.globalEndpointManager.ThinClientReadEndpoints + : this.globalEndpointManager.ReadEndpoints; return this.TryAddOrUpdatePartitionFailoverInfoAndMoveToNextLocation( partitionKeyRange, @@ -181,7 +183,9 @@ public override bool TryMarkEndpointUnavailableForPartitionKeyRange( else if (this.IsRequestEligibleForPerPartitionAutomaticFailover(request)) { // For any single master write accounts, the next locations to fail over will be the read regions configured at the account level. - ReadOnlyCollection nextLocations = this.globalEndpointManager.AccountReadEndpoints; + ReadOnlyCollection nextLocations = ConfigurationManager.IsThinClientEnabled(defaultValue: false) && ThinClientStoreModel.IsOperationSupportedByThinClient(request) + ? this.globalEndpointManager.ThinClientReadEndpoints + : this.globalEndpointManager.AccountReadEndpoints; return this.TryAddOrUpdatePartitionFailoverInfoAndMoveToNextLocation( partitionKeyRange, diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index 81b8545f6e..7c444fff83 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -1,62 +1,69 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Concurrent; - using System.IO; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Tracing; +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Concurrent; + using System.IO; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.FaultInjection; - using Newtonsoft.Json; - using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; - - /// - /// A TransportClient that sends requests to proxy endpoint. - /// And then processes the response back into DocumentServiceResponse objects. - /// - internal class ThinClientStoreClient : GatewayStoreClient - { + using Microsoft.Azure.Documents; + using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; + + /// + /// A TransportClient that sends requests to proxy endpoint. + /// And then processes the response back into DocumentServiceResponse objects. + /// + internal class ThinClientStoreClient : GatewayStoreClient + { + private readonly ObjectPool bufferProviderWrapperPool; - private readonly IChaosInterceptor chaosInterceptor; - - public ThinClientStoreClient( - CosmosHttpClient httpClient, - ICommunicationEventSource eventSource, + private readonly IChaosInterceptor chaosInterceptor; + private readonly bool isPartitionLevelFailoverEnabled; + private readonly ObjectPool bufferProviderWrapperPool; + + public ThinClientStoreClient( + CosmosHttpClient httpClient, + ICommunicationEventSource eventSource, JsonSerializerSettings serializerSettings = null, - IChaosInterceptor chaosInterceptor = null) - : base(httpClient, - eventSource, - serializerSettings) - { - this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); - this.chaosInterceptor = chaosInterceptor; - } - - public override async Task InvokeAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceType, - physicalAddress, - thinClientEndpoint, - globalDatabaseAccountName, - clientCollectionCache, - cancellationToken)) + bool isPartitionLevelFailoverEnabled = false, + IChaosInterceptor chaosInterceptor = null) + : base(httpClient, + eventSource, + serializerSettings, + isPartitionLevelFailoverEnabled) + { + this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; + this.chaosInterceptor = chaosInterceptor; + } + + public override async Task InvokeAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceType, + physicalAddress, + thinClientEndpoint, + globalDatabaseAccountName, + clientCollectionCache, + cancellationToken)) { if (this.chaosInterceptor != null) { @@ -69,43 +76,43 @@ public override async Task InvokeAsync( request.Headers.Remove("FAULTINJECTION_IS_PROXY"); return await ThinClientStoreClient.ParseResponseAsync(fiResponseMessage, request.SerializerSettings ?? base.SerializerSettings, request); } - } - HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); - return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); - } - } - - internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) - { - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? - HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : - HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); - - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceOperation.resourceType, - physicalAddress, - default, - default, - default, - default)) - { - return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); - } - } - - private async ValueTask PrepareRequestForProxyAsync( - DocumentServiceRequest request, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache) - { - HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; - requestMessage.Version = new Version(2, 0); - - BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); - try + } + HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); + return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); + } + } + + internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) + { + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? + HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : + HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); + + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceOperation.resourceType, + physicalAddress, + default, + default, + default, + default)) + { + return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); + } + } + + private async ValueTask PrepareRequestForProxyAsync( + DocumentServiceRequest request, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache) + { + HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; + requestMessage.Version = new Version(2, 0); + + BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); + try { PartitionKeyRange partitionKeyRange = request.RequestContext?.ResolvedPartitionKeyRange; @@ -119,80 +126,80 @@ private async ValueTask PrepareRequestForProxyAsync( ThinClientConstants.ProxyEndEpk, partitionKeyRange?.MaxExclusive); } - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyOperationType, - request.OperationType.ToOperationTypeString()); - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyResourceType, - request.ResourceType.ToResourceTypeString()); - - Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( - bufferProviderWrapper, - globalDatabaseAccountName, - clientCollectionCache, - requestMessage); - - if (!contentStream.CanSeek) - { - throw new InvalidOperationException( - $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); - } - - requestMessage.Content = new StreamContent(contentStream); - requestMessage.Content.Headers.ContentLength = contentStream.Length; + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyOperationType, + request.OperationType.ToOperationTypeString()); + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyResourceType, + request.ResourceType.ToResourceTypeString()); + + Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( + bufferProviderWrapper, + globalDatabaseAccountName, + clientCollectionCache, + requestMessage); + + if (!contentStream.CanSeek) + { + throw new InvalidOperationException( + $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); + } + + requestMessage.Content = new StreamContent(contentStream); + requestMessage.Content.Headers.ContentLength = contentStream.Length; - requestMessage.RequestUri = thinClientEndpoint; - requestMessage.Method = HttpMethod.Post; - - return requestMessage; - } - finally - { - this.bufferProviderWrapperPool.Return(bufferProviderWrapper); - } - } - - private Task InvokeClientAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); - return base.httpClient.SendHttpAsync( - () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), - resourceType, - HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), - request.RequestContext.ClientRequestStatistics, + requestMessage.RequestUri = thinClientEndpoint; + requestMessage.Method = HttpMethod.Post; + + return requestMessage; + } + finally + { + this.bufferProviderWrapperPool.Return(bufferProviderWrapper); + } + } + + private Task InvokeClientAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); + return base.httpClient.SendHttpAsync( + () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), + resourceType, + HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), + request.RequestContext.ClientRequestStatistics, cancellationToken, - request); - } - - internal class ObjectPool - { - private readonly ConcurrentBag Objects; - private readonly Func ObjectGenerator; - - public ObjectPool(Func objectGenerator) - { - this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); - this.Objects = new ConcurrentBag(); - } - - public T Get() - { - return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); - } - - public void Return(T item) - { - this.Objects.Add(item); - } - } - } + request); + } + + internal class ObjectPool + { + private readonly ConcurrentBag Objects; + private readonly Func ObjectGenerator; + + public ObjectPool(Func objectGenerator) + { + this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); + this.Objects = new ConcurrentBag(); + } + + public T Get() + { + return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); + } + + public void Return(T item) + { + this.Objects.Add(item); + } + } + } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs index b077d78e70..020011280d 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs @@ -20,6 +20,8 @@ namespace Microsoft.Azure.Cosmos /// internal class ThinClientStoreModel : GatewayStoreModel { + private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; + private readonly bool isPartitionLevelFailoverEnabled; private ThinClientStoreClient thinClientStoreClient; public ThinClientStoreModel( @@ -30,6 +32,7 @@ public ThinClientStoreModel( DocumentClientEventSource eventSource, JsonSerializerSettings serializerSettings, CosmosHttpClient httpClient, + bool isPartitionLevelFailoverEnabled = false, IChaosInterceptor chaosInterceptor) : base(endpointManager, sessionContainer, @@ -37,13 +40,19 @@ public ThinClientStoreModel( eventSource, serializerSettings, httpClient, - globalPartitionEndpointManager) + globalPartitionEndpointManager, + isPartitionLevelFailoverEnabled) { this.thinClientStoreClient = new ThinClientStoreClient( httpClient, eventSource, serializerSettings, + isPartitionLevelFailoverEnabled, chaosInterceptor); + this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; + this.globalPartitionEndpointManager = globalPartitionEndpointManager; + this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( + base.MarkEndpointsToHealthyAsync); } public override async Task ProcessMessageAsync( @@ -66,7 +75,6 @@ await GatewayStoreModel.ApplySessionTokenAsync( DocumentServiceResponse response; try { - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? base.GetFeedUri(request) : base.GetEntityUri(request); if (request.ResourceType.Equals(ResourceType.Document) && base.endpointManager.TryGetLocationForGatewayDiagnostics( request.RequestContext.LocationEndpointToRoute, out string regionName)) @@ -74,6 +82,23 @@ await GatewayStoreModel.ApplySessionTokenAsync( request.RequestContext.RegionName = regionName; } + // This is applicable for both per partition automatic failover and per partition circuit breaker. + if (this.isPartitionLevelFailoverEnabled + && !ReplicatedResourceClient.IsMasterResource(request.ResourceType) + && request.ResourceType.IsPartitioned()) + { + (bool isSuccess, PartitionKeyRange partitionKeyRange) = await GatewayStoreModel.TryResolvePartitionKeyRangeAsync( + request: request, + sessionContainer: this.sessionContainer, + partitionKeyRangeCache: this.partitionKeyRangeCache, + clientCollectionCache: this.clientCollectionCache, + refreshCache: false); + + request.RequestContext.ResolvedPartitionKeyRange = partitionKeyRange; + this.globalPartitionEndpointManager.TryAddPartitionLevelLocationOverride(request); + } + + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? base.GetFeedUri(request) : base.GetEntityUri(request); AccountProperties properties = await this.GetDatabaseAccountPropertiesAsync(); response = await this.thinClientStoreClient.InvokeAsync( request, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs index 5d8fee6085..eb68823ba0 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs @@ -29,8 +29,9 @@ public class CosmosItemIntegrationTests private static string region1; private static string region2; - private static string region3; + private static string region3; private IDictionary readRegionsMapping; + private IList thinClientreadRegionalEndpoints; private CosmosSystemTextJsonSerializer cosmosSystemTextJsonSerializer; [TestInitialize] @@ -482,8 +483,8 @@ public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountA { // Arrange. Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); - + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + // Enabling fault injection rule to simulate a 503 service unavailable scenario. string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( @@ -594,7 +595,7 @@ public async Task ReadItemAsync_WithCircuitBreakerEnabledAndSingleMasterAccountA finally { Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); - Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); await this.TryDeleteItems(itemsList); } @@ -1735,6 +1736,273 @@ public async Task AddressRefreshInternalServerErrorTest() } } } + + [TestMethod] + [TestCategory("MultiRegion")] + [Ignore("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] + [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task ReadItemAsync_WithThinClientCircuitBreakerEnabledAndSingleMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( + ConnectionMode connectionMode, + string iterationCount, + string circuitBreakerConsecutiveFailureCount) + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.ReadItem) + .WithRegion(Regions.WestUS) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = connectionMode, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); + + Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); + this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; + + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); + int totalIterations = int.Parse(iterationCount); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + ItemResponse readResponse = await container.ReadItemAsync( + id: itemsList[0].Id, + partitionKey: new PartitionKey(itemsList[0].Pk)); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: readResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + if (attemptCount == consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); + } + } + } + catch (CosmosException ce) + { + Assert.Fail("Read Item operation should succeed." + ce); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during ReadItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); + + await this.TryDeleteItems(itemsList); + } + } + + [TestMethod] + [TestCategory("MultiMaster")] + [Ignore ("We will enable this test once the test staging account used for multi master validation starts supporting thin proxy.")] + [DataRow(ConnectionMode.Gateway, "15", "10", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 15 and circuit breaker consecutive failure threshold is set to 10.")] + [DataRow(ConnectionMode.Gateway, "25", "20", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 25 and circuit breaker consecutive failure threshold is set to 20.")] + [DataRow(ConnectionMode.Gateway, "35", "30", DisplayName = "Thin Client Mode - Scenario when the total iteration count is 35 and circuit breaker consecutive failure threshold is set to 30.")] + [Owner("dkunda")] + [Timeout(70000)] + public async Task CreateItemAsync_WithThinClientEnabledAndCircuitBreakerEnabledAndMultiMasterAccountAndServiceUnavailableReceived_ShouldApplyPartitionLevelOverride( + ConnectionMode connectionMode, + string iterationCount, + string circuitBreakerConsecutiveFailureCount) + { + // Arrange. + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, "True"); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, circuitBreakerConsecutiveFailureCount); + + // Enabling fault injection rule to simulate a 503 service unavailable scenario. + string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString(); + FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder( + id: serviceUnavailableRuleId, + condition: + new FaultInjectionConditionBuilder() + .WithOperationType(FaultInjectionOperationType.CreateItem) + .WithRegion(Regions.WestUS) + .Build(), + result: + FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ServiceUnavailable) + .WithDelay(TimeSpan.FromMilliseconds(10)) + .Build()) + .Build(); + + List rules = new List { serviceUnavailableRule }; + FaultInjector faultInjector = new FaultInjector(rules); + + Random random = new(); + List itemsCleanupList = new(); + List preferredRegions = new List { Regions.WestUS, Regions.EastAsia }; + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() + { + ConnectionMode = connectionMode, + ConsistencyLevel = ConsistencyLevel.Session, + FaultInjector = faultInjector, + RequestTimeout = TimeSpan.FromSeconds(5), + ApplicationPreferredRegions = preferredRegions, + }; + + List itemsList = new() + { + new() { Id = "smTestId1", Pk = "smpk1" }, + }; + + try + { + using CosmosClient cosmosClient = new(connectionString: this.connectionString, clientOptions: cosmosClientOptions); + AccountProperties accountInfo = await cosmosClient.ReadAccountAsync(); + + Assert.IsTrue(cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints.Count() >= 2); + this.thinClientreadRegionalEndpoints = cosmosClient.DocumentClient.GlobalEndpointManager.ThinClientReadEndpoints; + + Database database = cosmosClient.GetDatabase(MultiRegionSetupHelpers.dbName); + Container container = database.GetContainer(MultiRegionSetupHelpers.containerName); + + // Act and Assert. + await this.TryCreateItems(itemsList); + + //Must Ensure the data is replicated to all regions + await Task.Delay(3000); + + int consecutiveFailureCount = int.Parse(circuitBreakerConsecutiveFailureCount); + int totalIterations = int.Parse(iterationCount); + + for (int attemptCount = 1; attemptCount <= totalIterations; attemptCount++) + { + try + { + CosmosIntegrationTestObject testItem = new() + { + Id = $"mmTestId{random.Next()}", + Pk = $"mmpk{random.Next()}" + }; + + ItemResponse createResponse = await container.CreateItemAsync(testItem); + itemsCleanupList.Add(testItem); + + Assert.AreEqual( + expected: HttpStatusCode.Created, + actual: createResponse.StatusCode); + + IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = createResponse.Diagnostics.GetContactedRegions(); + HashSet contactedRegions = new(contactedRegionMapping.Select(r => r.regionName)); + + Assert.AreEqual( + expected: HttpStatusCode.OK, + actual: createResponse.StatusCode); + + Assert.IsNotNull(contactedRegions); + + PartitionKeyRangeFailoverInfo failoverInfo = TestCommon.GetFailoverInfoForFirstPartitionUsingReflection( + globalPartitionEndpointManager: cosmosClient.ClientContext.DocumentClient.PartitionKeyRangeLocation, + isReadOnlyOrMultiMaster: true); + + if (attemptCount > consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + if (attemptCount == consecutiveFailureCount) + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[1], failoverInfo.Current); + } + else + { + Assert.AreEqual(this.thinClientreadRegionalEndpoints[0], failoverInfo.Current); + } + } + } + catch (CosmosException ce) + { + Assert.Fail("Create Item operation should succeed." + ce); + } + catch (Exception ex) + { + Assert.Fail($"Unhandled Exception was thrown during CreateItemAsync call. Message: {ex.Message}"); + } + } + } + finally + { + Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelCircuitBreakerEnabled, null); + Environment.SetEnvironmentVariable(ConfigurationManager.CircuitBreakerConsecutiveFailureCountForReads, null); + Environment.SetEnvironmentVariable(ConfigurationManager.ThinClientModeEnabled, null); + + await this.TryDeleteItems(itemsList); + } + } private async Task TryCreateItems(List testItems) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs index 3532b608aa..05f82bec36 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/RegionFailoverTests.cs @@ -301,6 +301,15 @@ public async Task ReadItemAsync_WithThinClientEnabledAndServiceUnavailableReceiv containerName: containerName, containerRid: containerRid); + if (enablePartitionLevelFailover) + { + MockSetupsHelper.SetupPartitionKeyRanges( + mockHttpHandler: mockHttpHandler, + regionEndpoint: primaryRegionEndpoint, + containerResourceId: containerResourceId, + partitionKeyRangeIds: out IReadOnlyList secondaryRegionPartitionKeyRangeIds); + } + CosmosClientOptions cosmosClientOptions = new CosmosClientOptions() { ConsistencyLevel = Cosmos.ConsistencyLevel.Strong, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index 1364409132..92d46ce910 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -4,8 +4,10 @@ namespace Microsoft.Azure.Cosmos.Tests { - using System; - using System.IO; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; @@ -13,7 +15,8 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -72,7 +75,7 @@ public void TestCleanup() this.thinClientStoreModel?.Dispose(); } - [TestMethod] + [TestMethod] public async Task ProcessMessageAsync_Success_ShouldReturnDocumentServiceResponse() { // Arrange @@ -252,7 +255,7 @@ public void Dispose_ShouldDisposeThinClientStoreClient() [TestMethod] public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() - { + { // Arrange MockThinClientStoreClient thinClientStoreClient = new MockThinClientStoreClient( (request, resourceType, uri, endpoint, globalDatabaseAccountName, clientCollectionCache, cancellationToken) => @@ -315,12 +318,150 @@ public async Task ProcessMessageAsync_404_ShouldThrowDocumentClientException() storeModel.SetCaches(partitionKeyRangeCache, clientCollectionCache); - ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); - - // Act & Assert + ReplaceThinClientStoreClientField(storeModel, thinClientStoreClient); + + // Act & Assert await Assert.ThrowsExceptionAsync( async () => await storeModel.ProcessMessageAsync(request), "Expected 404 DocumentClientException from the final thinClientStore call"); + } + + [TestMethod] + public async Task PartitionLevelFailoverEnabled_ResolvesPartitionKeyRangeAndCallsLocationOverride() + { + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + mockDocumentClient + .Setup(c => c.GetDatabaseAccountInternalAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccountProperties()); + + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); + + Mock globalPartitionEndpointManager = new Mock(); + globalPartitionEndpointManager + .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) + .Returns(true) + .Verifiable(); + + ISessionContainer sessionContainer = new Mock().Object; + DocumentClientEventSource eventSource = new Mock().Object; + Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); + CosmosHttpClient httpClient = new Mock().Object; + + ThinClientStoreModel storeModel = new ThinClientStoreModel( + endpointManager, + globalPartitionEndpointManager.Object, + sessionContainer, + Cosmos.ConsistencyLevel.Session, + eventSource, + serializerSettings, + httpClient, + isPartitionLevelFailoverEnabled: true); + + Mock mockCollectionCache = new Mock( + sessionContainer, + storeModel, + null, + null, + null, + false); + + ContainerProperties containerProperties = new ContainerProperties("test", "/pk"); + typeof(ContainerProperties) + .GetProperty("ResourceId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public) + ?.SetValue(containerProperties, "testCollectionRid"); + containerProperties.PartitionKeyPath = "/pk"; + + mockCollectionCache + .Setup(c => c.ResolveCollectionAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(containerProperties); + + Mock mockPartitionKeyRangeCache = new Mock( + null, + storeModel, + mockCollectionCache.Object, + endpointManager, + false); + + PartitionKeyRange pkRange = new PartitionKeyRange { Id = "0", MinInclusive = "", MaxExclusive = "FF" }; + List pkRanges = new List { pkRange }; + IEnumerable> rangeTuples = pkRanges.Select(r => Tuple.Create(r, (ServiceIdentity)null)); + CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(rangeTuples, "testCollectionRid"); + + mockPartitionKeyRangeCache + .Setup(c => c.TryLookupAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(routingMap); + + storeModel.SetCaches(mockPartitionKeyRangeCache.Object, mockCollectionCache.Object); + + DocumentServiceRequest request = CreatePartitionedDocumentRequest(); + + MockThinClientStoreClient mockThinClientStoreClient = new MockThinClientStoreClient( + (DocumentServiceRequest req, ResourceType resourceType, Uri uri, Uri endpoint, string globalDatabaseAccountName, ClientCollectionCache clientCollectionCache, CancellationToken cancellationToken) => + { + MemoryStream stream = new MemoryStream(new byte[] { 1, 2, 3 }); + INameValueCollection headers = new StoreResponseNameValueCollection(); + return Task.FromResult(new DocumentServiceResponse(stream, headers, HttpStatusCode.OK)); + }); + + ReplaceThinClientStoreClientField(storeModel, mockThinClientStoreClient); + + // Act + await storeModel.ProcessMessageAsync(request); + + // Assert + globalPartitionEndpointManager.Verify(m => m.TryAddPartitionLevelLocationOverride(It.IsAny()), Times.Once()); + } + + [TestMethod] + public void CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() + { + Mock mockDocumentClient = new Mock(); + mockDocumentClient.Setup(c => c.ServiceEndpoint).Returns(new Uri("https://mock.proxy.com")); + ConnectionPolicy connectionPolicy = new ConnectionPolicy(); + GlobalEndpointManager endpointManager = new GlobalEndpointManager(mockDocumentClient.Object, connectionPolicy); + Mock globalPartitionEndpointManager = new Mock(); + globalPartitionEndpointManager + .Setup(m => m.TryAddPartitionLevelLocationOverride(It.IsAny())) + .Returns(true); + + globalPartitionEndpointManager + .Setup(m => m.TryMarkEndpointUnavailableForPartitionKeyRange(It.IsAny())) + .Returns(true) + .Verifiable(); + + globalPartitionEndpointManager + .Setup(m => m.IncrementRequestFailureCounterAndCheckIfPartitionCanFailover(It.IsAny())) + .Returns(true); + + ISessionContainer sessionContainer = new Mock().Object; + DocumentClientEventSource eventSource = new Mock().Object; + Newtonsoft.Json.JsonSerializerSettings serializerSettings = new Newtonsoft.Json.JsonSerializerSettings(); + CosmosHttpClient httpClient = new Mock().Object; + + ThinClientStoreModel storeModel = new ThinClientStoreModel( + endpointManager, + globalPartitionEndpointManager.Object, + sessionContainer, + Cosmos.ConsistencyLevel.Session, + eventSource, + serializerSettings, + httpClient, + isPartitionLevelFailoverEnabled: true); + + TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); + + DocumentServiceRequest request = CreatePartitionedDocumentRequest(); + + for (int i = 0; i < 3; i++) + { + globalPartitionEndpointManager.Object.IncrementRequestFailureCounterAndCheckIfPartitionCanFailover(request); + } + + globalPartitionEndpointManager.Object.TryMarkEndpointUnavailableForPartitionKeyRange(request); + + globalPartitionEndpointManager.Verify(m => m.TryMarkEndpointUnavailableForPartitionKeyRange(It.IsAny()), Times.Once()); } private static void ReplaceThinClientStoreClientField(ThinClientStoreModel model, ThinClientStoreClient newClient) @@ -333,6 +474,18 @@ private static void ReplaceThinClientStoreClientField(ThinClientStoreModel model field.SetValue(model, newClient); } + private static DocumentServiceRequest CreatePartitionedDocumentRequest() + { + DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + "/dbs/test/colls/test/docs/test", + AuthorizationTokenType.PrimaryMasterKey); + request.Headers[HttpConstants.HttpHeaders.PartitionKey] = "[\"test\"]"; + request.RequestContext = new DocumentServiceRequestContext(); + return request; + } + internal class MockThinClientStoreClient : ThinClientStoreClient { private readonly Func> invokeAsyncFunc; From 5ad7d0470bedd2a339b0160907d8c23544256c3b Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Fri, 18 Jul 2025 11:16:29 -0700 Subject: [PATCH 08/20] Fixed build errors --- Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs | 6 +----- Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs | 2 +- .../ThinClientStoreModelTests.cs | 3 ++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index 7c444fff83..e8713046a7 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -12,10 +12,8 @@ namespace Microsoft.Azure.Cosmos using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.FaultInjection; - using Microsoft.Azure.Documents; using Newtonsoft.Json; using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; @@ -25,11 +23,9 @@ namespace Microsoft.Azure.Cosmos /// internal class ThinClientStoreClient : GatewayStoreClient { - private readonly ObjectPool bufferProviderWrapperPool; - private readonly IChaosInterceptor chaosInterceptor; private readonly bool isPartitionLevelFailoverEnabled; - private readonly ObjectPool bufferProviderWrapperPool; + private readonly IChaosInterceptor chaosInterceptor; public ThinClientStoreClient( CosmosHttpClient httpClient, diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs index 020011280d..959ee7ee73 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreModel.cs @@ -33,7 +33,7 @@ public ThinClientStoreModel( JsonSerializerSettings serializerSettings, CosmosHttpClient httpClient, bool isPartitionLevelFailoverEnabled = false, - IChaosInterceptor chaosInterceptor) + IChaosInterceptor chaosInterceptor = null) : base(endpointManager, sessionContainer, defaultConsistencyLevel, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs index 92d46ce910..33aa04d003 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ThinClientStoreModelTests.cs @@ -448,7 +448,8 @@ public void CircuitBreaker_MarksPartitionUnavailableOnRepeatedFailures() eventSource, serializerSettings, httpClient, - isPartitionLevelFailoverEnabled: true); + isPartitionLevelFailoverEnabled: true, + chaosInterceptor: null); TestUtils.SetupCachesInGatewayStoreModel(storeModel, endpointManager); From 780de28b2c0522854e40f3766ad99771fb473612 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:05:40 -0700 Subject: [PATCH 09/20] fix document client --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 14422 ++++++++--------- 1 file changed, 7211 insertions(+), 7211 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index d07329d231..b2add943f7 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1,6850 +1,6850 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Security; - using System.Security; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using global::Azure.Core; - using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Query; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Telemetry; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tracing.TraceData; - using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Client; - using Microsoft.Azure.Documents.Collections; - using Microsoft.Azure.Documents.FaultInjection; - using Microsoft.Azure.Documents.Routing; - using Newtonsoft.Json; - using ResourceType = Documents.ResourceType; - - /// - /// Provides a client-side logical representation for the Azure Cosmos DB service. - /// This client is used to configure and execute requests against the service. - /// - /// - /// This type is thread safe. - /// - /// - /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. - /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. - /// - /// - /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. - /// Depending on your programming technique, you can do this in one of two ways: - /// - /// - /// - /// 1. By using a language construct such as the using statement in C#. - /// The using statement is actually a syntactic convenience. - /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. - /// - /// - /// - /// - /// - /// - /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. - /// The following example replaces the using block in the previous example with a try/catch/finally block. - /// - /// - /// - /// - /// - /// - internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal - { - private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; - private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; - private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; - private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; - private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; - private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; - private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; - private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; - private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; - private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; - private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; - private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; - private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; - private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; - private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; - // Env variable - private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; - - private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; - private const int DefaultMaxRequestsPerRntbdChannel = 30; - private const int DefaultRntbdPartitionCount = 1; - private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; - private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; - private const int DefaultRntbdPortPoolReuseThreshold = 256; - private const int DefaultRntbdPortPoolBindAttempts = 5; - private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; - private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; - private const bool DefaultEnableCpuMonitor = true; - private const string DefaultInitTaskKey = "InitTaskKey"; - - /// - /// Default thresholds for PPAF request hedging. - /// - private const int DefaultHedgingThresholdInMilliseconds = 1000; - private const int DefaultHedgingThresholdStepInMilliseconds = 500; - - private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; - private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; - - private readonly bool IsLocalQuorumConsistency = false; - private readonly bool isReplicaAddressValidationEnabled; - private readonly bool enableAsyncCacheExceptionNoSharing; - - private readonly bool isThinClientEnabled; - - //Fault Injection - private readonly IChaosInterceptorFactory chaosInterceptorFactory; - private readonly IChaosInterceptor chaosInterceptor; - - private bool isChaosInterceptorInititalized = false; - - //Auth - internal readonly AuthorizationTokenProvider cosmosAuthorization; - - // Gateway has backoff/retry logic to hide transient errors. - private RetryPolicy retryPolicy; - private bool allowOverrideStrongerConsistency = false; - private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; - private int openConnectionTimeoutInSeconds = 5; - private int idleConnectionTimeoutInSeconds = -1; - private int timerPoolGranularityInSeconds = 1; - private bool enableRntbdChannel = true; - private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; - private int rntbdPartitionCount = DefaultRntbdPartitionCount; - private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; - private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; - private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; - private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - private bool enableCpuMonitor = DefaultEnableCpuMonitor; - private int rntbdMaxConcurrentOpeningConnectionCount = 5; - private string clientId; - - //Consistency - private Documents.ConsistencyLevel? desiredConsistencyLevel; - - internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } - - internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } - - private ClientCollectionCache collectionCache; - - private PartitionKeyRangeCache partitionKeyRangeCache; - - //Private state. - private bool isSuccessfullyInitialized; - private bool isDisposed; - - // creator of TransportClient is responsible for disposing it. - private IStoreClientFactory storeClientFactory; - internal CosmosHttpClient httpClient { get; private set; } - - // Flag that indicates whether store client factory must be disposed whenever client is disposed. - // Setting this flag to false will result in store client factory not being disposed when client is disposed. - // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. - private bool isStoreClientFactoryCreatedInternally; - - //Id counter. - private static int idCounter; - //Trace Id. - private int traceId; - - //RemoteCertificateValidationCallback - internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; - - //Distributed Tracing Flag - internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; - - //SessionContainer. - internal ISessionContainer sessionContainer; - - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - - private AsyncLazy queryPartitionProvider; - - private DocumentClientEventSource eventSource; - private Func> initializeTaskFactory; - internal AsyncCacheNonBlocking initTaskCache; - - private JsonSerializerSettings serializerSettings; - private event EventHandler sendingRequest; - private event EventHandler receivedResponse; - private Func transportClientHandlerFactory; - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - { - if (authKey == null) - { - throw new ArgumentNullException("authKey"); - } - - if (authKey != null) - { - this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); - } - - this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); - this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The connection policy for the client. - /// - /// - /// This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - HttpMessageHandler handler, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) - { - } - - internal DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null) - : this(serviceEndpoint, - AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), - sendingRequestEventArgs, - connectionPolicy, - desiredConsistencyLevel, - serializerSettings, - apitype, - receivedResponseEventArgs, - handler, - sessionContainer, - enableCpuMonitor, - transportClientHandlerFactory, - storeClientFactory) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The cosmos authorization for the client. - /// The event handler to be invoked before the request is sent. - /// The event handler to be invoked after a response has been received. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// Api type for the account - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// The default session container with which DocumentClient is created. - /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. - /// Transport client handler factory. - /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. - /// Flag to allow Quorum Read with Eventual Consistency Account - /// - /// This delegate responsible for validating the third party certificate. - /// This is distributed tracing flag - /// This is the chaos interceptor used for fault injection - /// A boolean flag indicating if stack trace optimization is enabled. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - internal DocumentClient(Uri serviceEndpoint, - AuthorizationTokenProvider cosmosAuthorization, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null, - bool isLocalQuorumConsistency = false, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - IChaosInterceptorFactory chaosInterceptorFactory = null, - bool enableAsyncCacheExceptionNoSharing = true) - { - if (sendingRequestEventArgs != null) - { - this.sendingRequest += sendingRequestEventArgs; - } - - if (serializerSettings != null) - { - this.serializerSettings = serializerSettings; - } - - this.ApiType = apitype; - - if (receivedResponseEventArgs != null) - { - this.receivedResponse += receivedResponseEventArgs; - } - - this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; - this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); - this.transportClientHandlerFactory = transportClientHandlerFactory; - this.IsLocalQuorumConsistency = isLocalQuorumConsistency; - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.chaosInterceptorFactory = chaosInterceptorFactory; - this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); - this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - - this.Initialize( - serviceEndpoint: serviceEndpoint, - connectionPolicy: connectionPolicy, - desiredConsistencyLevel: desiredConsistencyLevel, - handler: handler, - sessionContainer: sessionContainer, - enableCpuMonitor: enableCpuMonitor, - storeClientFactory: storeClientFactory, - cosmosClientId: cosmosClientId, - remoteCertificateValidationCallback: remoteCertificateValidationCallback, - cosmosClientTelemetryOptions: cosmosClientTelemetryOptions, - enableThinClientMode: this.isThinClientEnabled); - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The connection policy for the client. - /// The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Internal constructor purely for unit-testing - /// - internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) - { - // do nothing - this.ServiceEndpoint = serviceEndpoint; - this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); - } - - internal virtual async Task GetCollectionCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.collectionCache; - } - } - - internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.partitionKeyRangeCache; - } - } - - internal GlobalAddressResolver AddressResolver { get; private set; } - - internal GlobalEndpointManager GlobalEndpointManager { get; private set; } - - internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } - - /// - /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. - /// - /// - /// A object. - /// - /// - /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. - /// If there are any initialization exceptions, this method will throw them (set on the task). - /// Alternately, calling any API will throw initialization exception at the first call. - /// - /// - /// - /// - /// - /// - public Task OpenAsync(CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); - } - - private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); - } - - private async Task OpenPrivateAsync(CancellationToken cancellationToken) - { - // Initialize caches for all databases and collections - ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( - new FeedOptions { MaxItemCount = -1 }); - - try - { - while (databaseFeedReader.HasMoreResults) - { - foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) - { - ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( - database.SelfLink, - new FeedOptions { MaxItemCount = -1 }); - List tasks = new List(); - while (collectionFeedReader.HasMoreResults) - { - tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); - } - - await Task.WhenAll(tasks); - } - } - } - catch (DocumentClientException ex) - { - // Clear the caches to ensure that we don't have partial results - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - - DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); - } - } - - internal virtual void Initialize(Uri serviceEndpoint, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - IStoreClientFactory storeClientFactory = null, - TokenCredential tokenCredential = null, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - bool enableThinClientMode = false) - { - if (serviceEndpoint == null) - { - throw new ArgumentNullException("serviceEndpoint"); - } - - this.clientId = cosmosClientId; - this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; - this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); - - this.queryPartitionProvider = new AsyncLazy(async () => - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); - }, CancellationToken.None); - -#if !(NETSTANDARD15 || NETSTANDARD16) -#if NETSTANDARD20 - // GetEntryAssembly returns null when loaded from native netstandard2.0 - if (System.Reflection.Assembly.GetEntryAssembly() != null) - { -#endif - // For tests we want to allow stronger consistency during construction or per call - string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; - if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) - { - if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) - { - this.allowOverrideStrongerConsistency = false; - } - } - - // We might want to override the defaults sometime - string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; - if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) - { - int maxConcurrentConnectionOpenRequestOverrideInt = 0; - if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) - { - this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; - } - } - - string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) - { - int openConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) - { - this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; - } - } - - string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) - { - int idleConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) - { - this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; - } - } - - string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; - if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) - { - int timerPoolGranularityInSecondsOverrideInt = 0; - if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) - { - // timeoutgranularity specified should be greater than min(5 seconds) - if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) - { - this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; - } - } - } - - string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; - if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) - { - bool enableRntbdChannel = false; - if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) - { - this.enableRntbdChannel = enableRntbdChannel; - } - } - - string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; - if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) - { - int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; - if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) - { - this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; - } - } - - string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; - if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) - { - int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; - if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) - { - this.rntbdPartitionCount = rntbdPartitionCount; - } - } - - string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; - if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) - { - int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) - { - this.maxRntbdChannels = maxRntbdChannels; - } - } - - string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; - if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) - { - PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; - if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) - { - this.rntbdPortReuseMode = portReuseMode; - } - } - - string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; - if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) - { - int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; - if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) - { - this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; - } - } - - string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; - if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) - { - int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; - if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) - { - this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; - } - } - - string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) - { - int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) - { - this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; - } - } - - string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) - { - int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) - { - this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; - } - } - - if (enableCpuMonitor.HasValue) - { - this.enableCpuMonitor = enableCpuMonitor.Value; - } - else - { - string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; - if (!string.IsNullOrEmpty(enableCpuMonitorString)) - { - bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; - if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) - { - this.enableCpuMonitor = enableCpuMonitorFlag; - } - } - } -#if NETSTANDARD20 - } -#endif -#endif - - string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); - if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) - { - if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) - { - if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) - { - throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); - } - - this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; - } - } - - // ConnectionPolicy always overrides appconfig - if (connectionPolicy != null) - { - if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) - { - this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) - { - this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) - { - this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; - } - - if (connectionPolicy.MaxTcpPartitionCount.HasValue) - { - this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; - } - - if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) - { - this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; - } - - if (connectionPolicy.PortReuseMode.HasValue) - { - this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; - } - } - - this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); - - this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; - -#if !NETSTANDARD16 - if (ServicePointAccessor.IsSupported) - { - ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); - servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; - } -#endif - this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); - - this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( - this.ApiType, - DocumentClientEventSource.Instance, - this.ConnectionPolicy, - handler, - this.sendingRequest, - this.receivedResponse, - this.chaosInterceptor); - - // Loading VM Information (non blocking call and initialization won't fail if this call fails) - VmMetadataApiHandler.TryInitialize(this.httpClient); - - if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) - { - CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); - CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); - - CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); - } - - // Starting ClientTelemetry Job - this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, - this.ConnectionPolicy, - this.cosmosAuthorization, - this.httpClient, - this.ServiceEndpoint, - this.GlobalEndpointManager, - this.cancellationTokenSource, - this.chaosInterceptor is not null); - - if (sessionContainer != null) - { - this.sessionContainer = sessionContainer; - } - else - { - this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); - } - - this.desiredConsistencyLevel = desiredConsistencyLevel; - // Setup the proxy to be used based on connection mode. - // For gateway: GatewayProxy. - // For direct: WFStoreProxy [set in OpenAsync()]. - this.eventSource = DocumentClientEventSource.Instance; - - this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( - () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), - new ResourceThrottleRetryPolicy( - this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, - this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); - - // Create the task to start the initialize task - // Task will be awaited on in the EnsureValidClientAsync - Task initTask = this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - - // ContinueWith on the initialization task is needed for handling the UnobservedTaskException - // if this task throws for some reason. Awaiting inside a constructor is not supported and - // even if we had to await inside GetInitializationTask to catch the exception, that will - // be a blocking call. In such cases, the recommended approach is to "handle" the - // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted - // and accessing the Exception property on the target task. -#pragma warning disable VSTHRD110 // Observe result of async calls -#pragma warning disable CDX1000 // DontConvertExceptionToObject - initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); -#pragma warning restore CDX1000 // DontConvertExceptionToObject -#pragma warning restore VSTHRD110 // Observe result of async calls - - this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); - DefaultTrace.TraceInformation(string.Format( - CultureInfo.InvariantCulture, - "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", - this.traceId, - serviceEndpoint.ToString(), - this.ConnectionPolicy.ConnectionMode.ToString(), - this.ConnectionPolicy.ConnectionProtocol.ToString(), - desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); - - this.QueryCompatibilityMode = QueryCompatibilityMode.Default; - } - - // Always called from under the lock except when called from Intilialize method during construction. - private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) - { - await this.InitializeGatewayConfigurationReaderAsync(); - - if (this.desiredConsistencyLevel.HasValue) - { - this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); - } - - if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride - && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) - { - this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; - } - - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; - this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); - this.InitializePartitionLevelFailoverWithDefaultHedging(); - - this.PartitionKeyRangeLocation = - this.ConnectionPolicy.EnablePartitionLevelFailover - || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker - ? new GlobalPartitionEndpointManagerCore( - this.GlobalEndpointManager, - this.ConnectionPolicy.EnablePartitionLevelFailover, - this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) - : GlobalPartitionEndpointManagerNoOp.Instance; - - this.retryPolicy = new RetryPolicy( - globalEndpointManager: this.GlobalEndpointManager, - connectionPolicy: this.ConnectionPolicy, - partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation); - - this.ResetSessionTokenRetryPolicy = this.retryPolicy; - - GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( - this.GlobalEndpointManager, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); - - this.GatewayStoreModel = gatewayStoreModel; - - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); - - gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway && this.isThinClientEnabled) - { - ThinClientStoreModel thinClientStoreModel = new ( - endpointManager: this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, - chaosInterceptor: this.chaosInterceptor); - - thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); - - this.StoreModel = thinClientStoreModel; - } - else if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) - { - this.StoreModel = this.GatewayStoreModel; - } - else - { - this.InitializeDirectConnectivity(storeClientFactory); - } - - return true; - } - - private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) - { - if (databaseName == null) - { - throw new ArgumentNullException(nameof(databaseName)); - } - - if (collection == null) - { - throw new ArgumentNullException(nameof(collection)); - } - - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - using ( - DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Query, - ResourceType.Document, - collection.SelfLink, - AuthorizationTokenType.PrimaryMasterKey)) - { - ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( - resolvedCollection.ResourceId, - new Range( - PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, - PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, - true, - false), - NoOpTrace.Singleton); - - // In Gateway mode, AddressCache is null - if (this.AddressResolver != null) - { - await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); - } - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. - /// - /// - /// - /// The session object used for version tracking when the consistency level is set to Session. - /// - /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. - /// - public object Session - { - get - { - return this.sessionContainer; - } - - set - { - SessionContainer container = value as SessionContainer; - if (container == null) - { - throw new ArgumentNullException("value"); - } - - if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - ClientResources.BadSession, - container.HostName, - this.ServiceEndpoint.Host)); - } - - SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; - if (currentSessionContainer == null) - { - throw new ArgumentNullException(nameof(currentSessionContainer)); - } - - currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. - /// - /// Collection for which session token must be retrieved. - /// - /// The session token used for version tracking when the consistency level is set to Session. - /// - /// - /// The session token can be saved and supplied to a request via . - /// - internal string GetSessionToken(string collectionLink) - { - SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; - - if (sessionContainerInternal == null) - { - throw new ArgumentNullException(nameof(sessionContainerInternal)); - } - - return sessionContainerInternal.GetSessionToken(collectionLink); - } - - /// - /// Gets the Api type - /// - internal ApiType ApiType - { - get; private set; - } - - internal bool UseMultipleWriteLocations { get; private set; } - - /// - /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. - /// - /// - /// The Uri for the service endpoint. - /// - /// - public Uri ServiceEndpoint - { - get; - private set; - } - - /// - /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri WriteEndpoint - { - get - { - return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri ReadEndpoint - { - get - { - return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the Connection policy used by the client from the Azure Cosmos DB service. - /// - /// - /// The Connection policy used by the client. - /// - /// - public ConnectionPolicy ConnectionPolicy { get; private set; } - - /// - /// Gets the AuthKey used by the client from the Azure Cosmos DB service. - /// - /// - /// The AuthKey used by the client. - /// - /// - public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); - - /// - /// Gets the configured consistency level of the client from the Azure Cosmos DB service. - /// - /// - /// The configured of the client. - /// - /// - public virtual Documents.ConsistencyLevel ConsistencyLevel - { - get - { -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); -#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits - return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : - this.accountServiceConfiguration.DefaultConsistencyLevel; - } - } - - /// - /// Returns the account properties available in the service configuration if the client was initialized. - /// - public bool TryGetCachedAccountProperties(out AccountProperties properties) - { - if (this.isSuccessfullyInitialized - && this.accountServiceConfiguration != null - && this.accountServiceConfiguration.AccountProperties != null) - { - properties = this.accountServiceConfiguration.AccountProperties; - return true; - } - - properties = null; - return false; - } - - /// - /// Disposes the client for the Azure Cosmos DB service. - /// - /// - /// - /// - /// - /// - public void Dispose() - { - if (this.isDisposed) - { - return; - } - - if (this.telemetryToServiceHelper != null) - { - this.telemetryToServiceHelper.Dispose(); - this.telemetryToServiceHelper = null; - } - - if (!this.cancellationTokenSource.IsCancellationRequested) - { - this.cancellationTokenSource.Cancel(); - } - - this.cancellationTokenSource.Dispose(); - - if (this.StoreModel != null) - { - this.StoreModel.Dispose(); - this.StoreModel = null; - } - - if (this.storeClientFactory != null) - { - // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference - if (this.isStoreClientFactoryCreatedInternally) - { - this.storeClientFactory.Dispose(); - } - - this.storeClientFactory = null; - } - - if (this.AddressResolver != null) - { - this.AddressResolver.Dispose(); - this.AddressResolver = null; - } - - if (this.httpClient != null) - { - try - { - this.httpClient.Dispose(); - } - catch (Exception exception) - { - DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", - exception.Message); - } - - this.httpClient = null; - } - - if (this.cosmosAuthorization != null) - { - this.cosmosAuthorization.Dispose(); - } - - if (this.GlobalEndpointManager != null) - { - this.GlobalEndpointManager.Dispose(); - this.GlobalEndpointManager = null; - } - - if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) - { - this.queryPartitionProvider.Value.Dispose(); - } - - if (this.initTaskCache != null) - { - this.initTaskCache.Dispose(); - this.initTaskCache = null; - } - - DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); - DefaultTrace.Flush(); - - this.isDisposed = true; - } - - //Compatibility mode: - // Allows to specify compatibility mode used by client when making query requests. - // should be removed when application/sql is no longer supported. - internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } - - /// - /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). - /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache - /// to resolves the request's collection name. If it differs from the session container's resource id it - /// explains the session unavailable exception: somebody removed and recreated the collection. In this - /// case we retry once again (with empty session token) otherwise we return the error to the client - /// (see RenameCollectionAwareClientRetryPolicy) - /// - internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } - - /// - /// Gets and sets the IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension StoreModel { get; set; } - - /// - /// Gets and sets the gateway IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension GatewayStoreModel { get; set; } - - /// - /// Gets and sets on execute scalar query callback - /// - /// - /// Test hook to enable unit test for scalar queries - /// - internal Action OnExecuteScalarQueryCallback { get; set; } - - internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; - - internal virtual async Task GetDefaultConsistencyLevelAsync() - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; - } - - internal Task GetDesiredConsistencyLevelAsync() - { - return Task.FromResult(this.desiredConsistencyLevel); - } - - internal async Task ProcessRequestAsync( - string verb, - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - string testAuthorization = null) // Only for unit-tests - { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (verb == null) - { - throw new ArgumentNullException(nameof(verb)); - } - - (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( - request.ResourceAddress, - PathsHelper.GetResourcePath(request.ResourceType), - verb, - request.Headers, - AuthorizationTokenType.PrimaryMasterKey); - - // Unit-test hook - if (testAuthorization != null) - { - payload = testAuthorization; - authorization = testAuthorization; - } - request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; - - try - { - return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); - } - catch (DocumentClientException dce) - { - this.cosmosAuthorization.TraceUnauthorized( - dce, - authorization, - payload); - - throw; - } - } - - internal Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); - } - - internal async Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - ITrace trace, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(trace); - - retryPolicyInstance?.OnBeforeSendRequest(request); - - using (new ActivityScope(Guid.NewGuid())) - { - IStoreModel storeProxy = this.GetStoreProxy(request); - return await storeProxy.ProcessMessageAsync(request, cancellationToken); - } - } - - /// - /// Establishes and Initializes the Rntbd connection to all the backend replica nodes - /// for the given database name and container. - /// - /// A string containing the cosmos database name. - /// A string containing the cosmos container link uri. - /// An instance of the . - internal async Task OpenConnectionsToAllReplicasAsync( - string databaseName, - string containerLinkUri, - CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(databaseName) || - string.IsNullOrEmpty(containerLinkUri)) - { - string resourceName = string.IsNullOrEmpty(databaseName) ? - nameof(databaseName) : - nameof(containerLinkUri); - - throw new ArgumentNullException(resourceName); - } - - if (this.StoreModel != null) - { - try - { - await this.StoreModel.OpenConnectionsToAllReplicasAsync( - databaseName, - containerLinkUri, - cancellationToken); - } - catch (Exception) - { - throw; - } - } - } - - private static string NormalizeAuthorizationPayload(string input) - { - const int expansionBuffer = 12; - StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); - for (int i = 0; i < input.Length; i++) - { - switch (input[i]) - { - case '\n': - builder.Append("\\n"); - break; - case '/': - builder.Append("\\/"); - break; - default: - builder.Append(input[i]); - break; - } - } - - return builder.ToString(); - } - - internal async Task InitilizeFaultInjectionAsync() - { - if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) - { - this.isChaosInterceptorInititalized = true; - await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); - } - } - - internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) - { - return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.maxRequestsPerRntbdChannel, - this.maxRntbdChannels, - this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - this.rntbdPortReuseMode); - } - - internal virtual async Task EnsureValidClientAsync(ITrace trace) - { - if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) - { - return; - } - - // Trace when the Initialization of client has not been completed. Usually during first call - using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) - { - // If the initialization task failed, we should retry initialization. - // We may end up throwing the same exception but this will ensure that we dont have a - // client which is unusable and can resume working if it failed initialization once. - // If we have to reinitialize the client, it needs to happen in thread safe manner so that - // we dont re-initalize the task again for each incoming call. - try - { - this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - } - catch (DocumentClientException ex) - { - throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( - dce: ex, - trace: trace); - } - catch (Exception e) - { - DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); - childTrace.AddDatum("initializeTask failed", e.Message); - throw; - } - - await this.InitilizeFaultInjectionAsync(); - } - } - - #region Create Impl - /// - /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed. - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); - } - - private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, - Documents.Client.RequestOptions options) - { - if (database == null) - { - throw new ArgumentNullException("database"); - } - - // Doing a Read before Create will give us better latency for existing databases - try - { - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDatabaseAsync(database, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - - /// - /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to create. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// Finally, a Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Create a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, - object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, - CancellationToken cancellationToken = default) - { - // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy); - } - - private async Task> CreateDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 collection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDocumentCollectionPrivateAsync( - string databaseLink, - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse( - await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 DocumentCollection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); - } - - private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( - string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) - { - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make - // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. - Documents.Database database = await this.ReadDatabaseAsync(databaseLink); - - // Doing a Read before Create will give us better latency for existing collections. - // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, - // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. - try - { - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - - /// - /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link to the source object. - /// The target object. - /// (optional)The point in time to restore. If null, use the latest restorable time. - /// (Optional) The for the request. - /// The task object representing the service response for the asynchronous operation. - internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - if (targetDocumentCollection == null) - { - throw new ArgumentNullException("targetDocumentCollection"); - } - - bool isFeed; - string resourceTypeString; - string resourceIdOrFullName; - bool isNameBased; - - string dbsId; - string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); - if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - dbsId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - string sourceCollId; - if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - sourceCollId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - this.ValidateResource(targetDocumentCollection); - - if (options == null) - { - options = new Documents.Client.RequestOptions(); - } - if (!options.RemoteStorageType.HasValue) - { - options.RemoteStorageType = RemoteStorageType.Standard; - } - options.SourceDatabaseId = dbsId; - options.SourceCollectionId = sourceCollId; - if (restoreTime.HasValue) - { - options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - targetDocumentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Get the status of a collection being restored in the Azure Cosmos DB service. - /// - /// The link of the document collection being restored. - /// The task object representing the service response for the asynchronous operation. - internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (string.IsNullOrEmpty(targetDocumentCollectionLink)) - { - throw new ArgumentNullException("targetDocumentCollectionLink"); - } - - ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( - targetDocumentCollectionLink, - new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, - retryPolicyInstance); - string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); - if (restoreState == null) - { - restoreState = RestoreState.RestoreCompleted.ToString(); - } - - DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() - { - State = restoreState - }; - - return ret; - } - - /// - /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ - /// The object to create. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the created object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. - /// - /// - /// 409 - /// - /// Conflict - This means a with an id matching the id field of already existed, - /// or there is already a pending snapshot for the specified resource link. - /// - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that - /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (snapshot == null) - { - throw new ArgumentNullException("snapshot"); - } - - this.ValidateResource(snapshot); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Snapshots_Root, - snapshot, - ResourceType.Snapshot, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Delete Impl - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. snapshots/snapshot_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Replace Impl - /// - /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. - /// - /// the updated document collection. - /// the request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceDocumentCollectionPrivateAsync( - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(documentCollection), - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - // set the session token - if (collection.Resource != null) - { - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - } - return collection; - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If either or is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now dynamically cast doc back to your MyPoco - /// MyPoco poco = (dynamic)doc; - /// - /// //Update some properties of the poco object - /// poco.MyProperty = "updated value"; - /// - /// //Now persist these changes to the database using doc.SelLink and the update poco object - /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if ((options == null) || (options.PartitionKey == null)) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible( - () => this.ReplaceDocumentPrivateAsync( - documentLink, - document, - options, - requestRetryPolicy, - cancellationToken), - requestRetryPolicy, - cancellationToken); - } - - private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// doc.SetPropertyValue("MyProperty", "updated value"); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Document updated = await client.ReplaceDocumentAsync(doc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( - this.GetLinkForRouting(document), - document, - options, - retryPolicyInstance, - cancellationToken), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - this.ValidateResource(document); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - documentLink, - document, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, document, options); - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "sproc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// sproc.Body = "function () {new javascript body for sproc}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceStoredProcedurePrivateAsync( - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(storedProcedure), - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "trigger id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// trigger.Body = "function () {new javascript body for trigger}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// Trigger updated = await client.ReplaceTriggerAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(trigger), - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "udf id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// udf.Body = "function () {new javascript body for udf}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedFunctionPrivateAsync( - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(function), - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. - /// - /// - /// - /// - /// - /// r.ResourceLink == "collection selfLink") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Create a new offer with the changed throughput - /// OfferV2 newOffer = new OfferV2(offer, 5000); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Offer updated = await client.ReplaceOfferAsync(newOffer); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceOfferAsync(Offer offer) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (offer == null) - { - throw new ArgumentNullException("offer"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - offer.SelfLink, - offer, - ResourceType.Offer, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse( - await this.UpdateAsync(request, retryPolicyInstance), - OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "user defined type id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(userDefinedType), - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Read Impl - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Database resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// (docLink); - /// ]]> - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the DocumentCollection to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionPrivateAsync( - string documentCollectionLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the stored procedure to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Trigger to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the User Defined Function to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Conflict to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads an from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Offer to be read. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. - /// - /// - /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about - /// minimum throughput of a Cosmos container (or a database) - /// To retrieve the minimum throughput for a collection/database, use the following sample - /// - /// response = await client.ReadOfferAsync(offer.SelfLink); - /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; - /// ]]> - /// - /// - /// - /// - /// - /// - /// - /// - public Task> ReadOfferAsync(string offerLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(offerLink)) - { - throw new ArgumentNullException("offerLink"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Offer, - offerLink, - null, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Reads a as an asynchronous operation. - /// - /// The link for the schema to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Schema are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentSchemaLink)) - { - throw new ArgumentNullException("documentSchemaLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Schema, - documentSchemaLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the UserDefinedType resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypeLink)) - { - throw new ArgumentNullException("userDefinedTypeLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedType, - userDefinedTypeLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Snapshot resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Snapshot are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/snapshots/{snapshot identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region ReadFeed Impl - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDatabaseFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = null; - /// List ids = new List(); - /// do - /// { - /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); - /// foreach (var item in response) - /// { - /// ids.Add(item.Id); - /// } - /// } - /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(partitionKeyRangesLink)) - { - throw new ArgumentNullException("partitionKeyRangesLink"); - } - - return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionsLink)) - { - throw new ArgumentNullException("collectionsLink"); - } - - return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProceduresLink)) - { - throw new ArgumentNullException("storedProceduresLink"); - } - - return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggersLink)) - { - throw new ArgumentNullException("triggersLink"); - } - - return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedFunctionsLink)) - { - throw new ArgumentNullException("userDefinedFunctionsLink"); - } - - return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. - /// This takes returns a which will contain an enumerable list of dynamic objects. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a containing dynamic objects representing the items in the feed. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. - /// This is important becuse a DocumentCollection can contain different kinds of documents. - /// - /// - /// - /// - public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentsLink)) - { - throw new ArgumentNullException("documentsLink"); - } - - DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); - return new DocumentFeedResponse( - response.Cast(), - response.Count, - response.Headers, - response.UseETagAsContinuation, - response.QueryMetrics, - response.RequestStatistics, - responseLengthBytes: response.ResponseLengthBytes); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); - } - - private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictsLink)) - { - throw new ArgumentNullException("conflictsLink"); - } - - return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service - /// as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadOfferAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadOffersFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionSchemaLink)) - { - throw new ArgumentNullException("documentCollectionSchemaLink"); - } - - return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypesLink)) - { - throw new ArgumentNullException("userDefinedTypesLink"); - } - - return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a set of containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadSnapshotFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); - } - - #endregion - - #region Stored procs - /// - /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) - { - return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - default, - procedureParams), - retryPolicyInstance); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - cancellationToken, - procedureParams), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ExecuteStoredProcedurePrivateAsync( - string storedProcedureLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - params dynamic[] procedureParams) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); - string storedProcedureInput = serializerSettings == null ? - JsonConvert.SerializeObject(procedureParams) : - JsonConvert.SerializeObject(procedureParams, serializerSettings); - using (MemoryStream storedProcedureInputStream = new MemoryStream()) - { - using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) - { - await writer.WriteAsync(storedProcedureInput); - await writer.FlushAsync(); - storedProcedureInputStream.Position = 0; - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.ExecuteJavaScript, - ResourceType.StoredProcedure, - storedProcedureLink, - storedProcedureInputStream, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); - if (options?.PartitionKeyRangeId == null) - { - await this.AddPartitionKeyInformationAsync( - request, - options); - } - - retryPolicyInstance?.OnBeforeSendRequest(request); - - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new StoredProcedureResponse(await this.ExecuteProcedureAsync( - request, - retryPolicyInstance, - cancellationToken), - this.GetSerializerSettingsForRequest(options)); - } - } - } - } - - #endregion - - #region Upsert Impl - /// - /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to upsert. - /// (Optional) The for the request. - /// The that was upserted within a task object representing the service response for the asynchronous operation. - /// If is not set - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// - /// - /// The example below upserts a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to upsert. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// A Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Upsert a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) - { - // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy, cancellationToken); - } - - private async Task> UpsertDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - // To be implemented. - throw new NotImplementedException(); - } - - /// - /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. - /// - /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ - /// The object to upsert. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the upserted object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - #endregion - - #region IAuthorizationTokenProvider - - ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType) - { - return this.cosmosAuthorization.GetUserAuthorizationAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType); - } - - ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType, - ITrace trace) - { - return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType, - trace); - } - - Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( - DocumentServiceRequest request, - string federationId, - string verb, - string resourceId) - { - return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( - request, - federationId, - verb, - resourceId); - } - - #endregion - - #region Core Implementation - internal Task CreateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpdateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); - } - - internal Task ReadAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task ReadFeedAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task DeleteAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteProcedureAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteQueryAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpsertAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - #endregion - - /// - /// Read the from the Azure Cosmos DB service as an asynchronous operation. - /// - /// - /// A wrapped in a object. - /// - public Task GetDatabaseAccountAsync() - { - return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); - } - - /// - /// Read the as an asynchronous operation - /// given a specific reginal endpoint url. - /// - /// The reginal url of the serice endpoint. - /// The CancellationToken - /// - /// A wrapped in a object. - /// - Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) - { - return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); - } - - private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) - { - async ValueTask CreateRequestMessage() - { - HttpRequestMessage request = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = serviceEndpoint - }; - - INameValueCollection headersCollection = new StoreResponseNameValueCollection(); - await this.cosmosAuthorization.AddAuthorizationHeaderAsync( - headersCollection, - serviceEndpoint, - "GET", - AuthorizationTokenType.PrimaryMasterKey); - - foreach (string key in headersCollection.AllKeys()) - { - request.Headers.Add(key, headersCollection[key]); - } - - return request; - } - - AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, - clientSideRequestStatistics: null); - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; - - if (this.queryPartitionProvider.IsValueCreated) - { - (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); - } - - return databaseAccount; - } - - return null; - } - - #region Private Impl - - /// - /// Certain requests must be routed through gateway even when the client connectivity mode is direct. - /// For e.g., DocumentCollection creation. This method returns the based - /// on the input . - /// - /// Returns to which the request must be sent - internal IStoreModel GetStoreProxy(DocumentServiceRequest request) - { - // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) - // we return the Gateway store model - if (request.UseGatewayMode) - { - return this.GatewayStoreModel; - } - - ResourceType resourceType = request.ResourceType; - OperationType operationType = request.OperationType; - - if (resourceType == ResourceType.Offer || - (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || - resourceType == ResourceType.PartitionKeyRange || - resourceType == ResourceType.Snapshot || - resourceType == ResourceType.ClientEncryptionKey || - (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) - { - return this.GatewayStoreModel; - } - - if (this.isThinClientEnabled - && operationType == OperationType.Read - && resourceType == ResourceType.Database) - { - return this.GatewayStoreModel; - } - - if (operationType == OperationType.Create - || operationType == OperationType.Upsert) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection || - resourceType == ResourceType.Permission) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Delete) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Read) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else - { - return this.StoreModel; - } - } - - /// - /// The preferred link used in replace operation in SDK. +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Net.Security; + using System.Security; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Common; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Query; + using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Tracing.TraceData; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Client; + using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.FaultInjection; + using Microsoft.Azure.Documents.Routing; + using Newtonsoft.Json; + using ResourceType = Documents.ResourceType; + + /// + /// Provides a client-side logical representation for the Azure Cosmos DB service. + /// This client is used to configure and execute requests against the service. + /// + /// + /// This type is thread safe. + /// + /// + /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. + /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. + /// + /// + /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. + /// Depending on your programming technique, you can do this in one of two ways: + /// + /// + /// + /// 1. By using a language construct such as the using statement in C#. + /// The using statement is actually a syntactic convenience. + /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. + /// + /// + /// + /// + /// + /// + /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. + /// The following example replaces the using block in the previous example with a try/catch/finally block. + /// + /// + /// + /// + /// + /// + internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal + { + private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; + private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; + private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; + private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; + private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; + private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; + private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; + private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; + private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; + private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; + private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; + private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; + private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; + private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; + private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; + // Env variable + private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; + + private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; + private const int DefaultMaxRequestsPerRntbdChannel = 30; + private const int DefaultRntbdPartitionCount = 1; + private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; + private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; + private const int DefaultRntbdPortPoolReuseThreshold = 256; + private const int DefaultRntbdPortPoolBindAttempts = 5; + private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; + private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; + private const bool DefaultEnableCpuMonitor = true; + private const string DefaultInitTaskKey = "InitTaskKey"; + + /// + /// Default thresholds for PPAF request hedging. /// - private string GetLinkForRouting(Documents.Resource resource) - { - // we currently prefer the selflink - return resource.SelfLink ?? resource.AltLink; - } - - internal void EnsureValidOverwrite( - Documents.ConsistencyLevel desiredConsistencyLevel, - OperationType? operationType = null, - ResourceType? resourceType = null) - { - Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; - if (!this.IsValidConsistency( - defaultConsistencyLevel, - desiredConsistencyLevel, - operationType, - resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - desiredConsistencyLevel.ToString(), - defaultConsistencyLevel.ToString())); - } - } - - private bool IsValidConsistency( - Documents.ConsistencyLevel backendConsistency, - Documents.ConsistencyLevel desiredConsistency, - OperationType? operationType, - ResourceType? resourceType) - { - if (this.allowOverrideStrongerConsistency) - { - return true; - } - - return ValidationHelpers.IsValidConsistencyLevelOverwrite( - backendConsistency: backendConsistency, - desiredConsistency: desiredConsistency, - isLocalQuorumConsistency: this.IsLocalQuorumConsistency, - operationType: operationType, - resourceType: resourceType); - } - - private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) - { - // Check if we have a store client factory in input and if we do, do not initialize another store client - // The purpose is to reuse store client factory across all document clients inside compute gateway - if (storeClientFactory != null) - { - this.storeClientFactory = storeClientFactory; - this.isStoreClientFactoryCreatedInternally = false; - } - else - { - // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, - // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. - Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () - { -#if INTERNAL - IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing -#else - IsDistributedTracingEnabled = false + private const int DefaultHedgingThresholdInMilliseconds = 1000; + private const int DefaultHedgingThresholdStepInMilliseconds = 500; + + private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; + private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; + + private readonly bool IsLocalQuorumConsistency = false; + private readonly bool isReplicaAddressValidationEnabled; + private readonly bool enableAsyncCacheExceptionNoSharing; + + private readonly bool isThinClientEnabled; + + //Fault Injection + private readonly IChaosInterceptorFactory chaosInterceptorFactory; + private readonly IChaosInterceptor chaosInterceptor; + + private bool isChaosInterceptorInititalized = false; + + //Auth + internal readonly AuthorizationTokenProvider cosmosAuthorization; + + // Gateway has backoff/retry logic to hide transient errors. + private RetryPolicy retryPolicy; + private bool allowOverrideStrongerConsistency = false; + private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; + private int openConnectionTimeoutInSeconds = 5; + private int idleConnectionTimeoutInSeconds = -1; + private int timerPoolGranularityInSeconds = 1; + private bool enableRntbdChannel = true; + private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; + private int rntbdPartitionCount = DefaultRntbdPartitionCount; + private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; + private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; + private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; + private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + private bool enableCpuMonitor = DefaultEnableCpuMonitor; + private int rntbdMaxConcurrentOpeningConnectionCount = 5; + private string clientId; + + //Consistency + private Documents.ConsistencyLevel? desiredConsistencyLevel; + + internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } + + internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } + + private ClientCollectionCache collectionCache; + + private PartitionKeyRangeCache partitionKeyRangeCache; + + //Private state. + private bool isSuccessfullyInitialized; + private bool isDisposed; + + // creator of TransportClient is responsible for disposing it. + private IStoreClientFactory storeClientFactory; + internal CosmosHttpClient httpClient { get; private set; } + + // Flag that indicates whether store client factory must be disposed whenever client is disposed. + // Setting this flag to false will result in store client factory not being disposed when client is disposed. + // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. + private bool isStoreClientFactoryCreatedInternally; + + //Id counter. + private static int idCounter; + //Trace Id. + private int traceId; + + //RemoteCertificateValidationCallback + internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; + + //Distributed Tracing Flag + internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; + + //SessionContainer. + internal ISessionContainer sessionContainer; + + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + private AsyncLazy queryPartitionProvider; + + private DocumentClientEventSource eventSource; + private Func> initializeTaskFactory; + internal AsyncCacheNonBlocking initTaskCache; + + private JsonSerializerSettings serializerSettings; + private event EventHandler sendingRequest; + private event EventHandler receivedResponse; + private Func transportClientHandlerFactory; + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + { + if (authKey == null) + { + throw new ArgumentNullException("authKey"); + } + + if (authKey != null) + { + this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); + } + + this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); + this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The connection policy for the client. + /// + /// + /// This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + HttpMessageHandler handler, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) + { + } + + internal DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null) + : this(serviceEndpoint, + AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), + sendingRequestEventArgs, + connectionPolicy, + desiredConsistencyLevel, + serializerSettings, + apitype, + receivedResponseEventArgs, + handler, + sessionContainer, + enableCpuMonitor, + transportClientHandlerFactory, + storeClientFactory) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The cosmos authorization for the client. + /// The event handler to be invoked before the request is sent. + /// The event handler to be invoked after a response has been received. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// Api type for the account + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// The default session container with which DocumentClient is created. + /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. + /// Transport client handler factory. + /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. + /// Flag to allow Quorum Read with Eventual Consistency Account + /// + /// This delegate responsible for validating the third party certificate. + /// This is distributed tracing flag + /// This is the chaos interceptor used for fault injection + /// A boolean flag indicating if stack trace optimization is enabled. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + internal DocumentClient(Uri serviceEndpoint, + AuthorizationTokenProvider cosmosAuthorization, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null, + bool isLocalQuorumConsistency = false, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + IChaosInterceptorFactory chaosInterceptorFactory = null, + bool enableAsyncCacheExceptionNoSharing = true) + { + if (sendingRequestEventArgs != null) + { + this.sendingRequest += sendingRequestEventArgs; + } + + if (serializerSettings != null) + { + this.serializerSettings = serializerSettings; + } + + this.ApiType = apitype; + + if (receivedResponseEventArgs != null) + { + this.receivedResponse += receivedResponseEventArgs; + } + + this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; + this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); + this.transportClientHandlerFactory = transportClientHandlerFactory; + this.IsLocalQuorumConsistency = isLocalQuorumConsistency; + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.chaosInterceptorFactory = chaosInterceptorFactory; + this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); + this.isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + + this.Initialize( + serviceEndpoint: serviceEndpoint, + connectionPolicy: connectionPolicy, + desiredConsistencyLevel: desiredConsistencyLevel, + handler: handler, + sessionContainer: sessionContainer, + enableCpuMonitor: enableCpuMonitor, + storeClientFactory: storeClientFactory, + cosmosClientId: cosmosClientId, + remoteCertificateValidationCallback: remoteCertificateValidationCallback, + cosmosClientTelemetryOptions: cosmosClientTelemetryOptions, + enableThinClientMode: this.isThinClientEnabled); + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The connection policy for the client. + /// The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Internal constructor purely for unit-testing + /// + internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) + { + // do nothing + this.ServiceEndpoint = serviceEndpoint; + this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); + } + + internal virtual async Task GetCollectionCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.collectionCache; + } + } + + internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.partitionKeyRangeCache; + } + } + + internal GlobalAddressResolver AddressResolver { get; private set; } + + internal GlobalEndpointManager GlobalEndpointManager { get; private set; } + + internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } + + /// + /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. + /// + /// + /// A object. + /// + /// + /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. + /// If there are any initialization exceptions, this method will throw them (set on the task). + /// Alternately, calling any API will throw initialization exception at the first call. + /// + /// + /// + /// + /// + /// + public Task OpenAsync(CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); + } + + private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); + } + + private async Task OpenPrivateAsync(CancellationToken cancellationToken) + { + // Initialize caches for all databases and collections + ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( + new FeedOptions { MaxItemCount = -1 }); + + try + { + while (databaseFeedReader.HasMoreResults) + { + foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) + { + ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( + database.SelfLink, + new FeedOptions { MaxItemCount = -1 }); + List tasks = new List(); + while (collectionFeedReader.HasMoreResults) + { + tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); + } + + await Task.WhenAll(tasks); + } + } + } + catch (DocumentClientException ex) + { + // Clear the caches to ensure that we don't have partial results + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + + DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); + } + } + + internal virtual void Initialize(Uri serviceEndpoint, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + IStoreClientFactory storeClientFactory = null, + TokenCredential tokenCredential = null, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + bool enableThinClientMode = false) + { + if (serviceEndpoint == null) + { + throw new ArgumentNullException("serviceEndpoint"); + } + + this.clientId = cosmosClientId; + this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; + this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); + + this.queryPartitionProvider = new AsyncLazy(async () => + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); + }, CancellationToken.None); + +#if !(NETSTANDARD15 || NETSTANDARD16) +#if NETSTANDARD20 + // GetEntryAssembly returns null when loaded from native netstandard2.0 + if (System.Reflection.Assembly.GetEntryAssembly() != null) + { +#endif + // For tests we want to allow stronger consistency during construction or per call + string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; + if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) + { + if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) + { + this.allowOverrideStrongerConsistency = false; + } + } + + // We might want to override the defaults sometime + string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; + if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) + { + int maxConcurrentConnectionOpenRequestOverrideInt = 0; + if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) + { + this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; + } + } + + string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) + { + int openConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) + { + this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; + } + } + + string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) + { + int idleConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) + { + this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; + } + } + + string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; + if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) + { + int timerPoolGranularityInSecondsOverrideInt = 0; + if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) + { + // timeoutgranularity specified should be greater than min(5 seconds) + if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) + { + this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; + } + } + } + + string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; + if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) + { + bool enableRntbdChannel = false; + if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) + { + this.enableRntbdChannel = enableRntbdChannel; + } + } + + string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; + if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) + { + int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; + if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) + { + this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; + } + } + + string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; + if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) + { + int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; + if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) + { + this.rntbdPartitionCount = rntbdPartitionCount; + } + } + + string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; + if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) + { + int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) + { + this.maxRntbdChannels = maxRntbdChannels; + } + } + + string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; + if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) + { + PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; + if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) + { + this.rntbdPortReuseMode = portReuseMode; + } + } + + string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; + if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) + { + int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; + if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) + { + this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; + } + } + + string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; + if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) + { + int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; + if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) + { + this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; + } + } + + string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) + { + int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) + { + this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; + } + } + + string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) + { + int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) + { + this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; + } + } + + if (enableCpuMonitor.HasValue) + { + this.enableCpuMonitor = enableCpuMonitor.Value; + } + else + { + string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; + if (!string.IsNullOrEmpty(enableCpuMonitorString)) + { + bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; + if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) + { + this.enableCpuMonitor = enableCpuMonitorFlag; + } + } + } +#if NETSTANDARD20 + } +#endif +#endif + + string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); + if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) + { + if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) + { + if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) + { + throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); + } + + this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; + } + } + + // ConnectionPolicy always overrides appconfig + if (connectionPolicy != null) + { + if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) + { + this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) + { + this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) + { + this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; + } + + if (connectionPolicy.MaxTcpPartitionCount.HasValue) + { + this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; + } + + if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) + { + this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; + } + + if (connectionPolicy.PortReuseMode.HasValue) + { + this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; + } + } + + this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); + + this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; + +#if !NETSTANDARD16 + if (ServicePointAccessor.IsSupported) + { + ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); + servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; + } #endif - - }; - - StoreClientFactory newClientFactory = new StoreClientFactory( - this.ConnectionPolicy.ConnectionProtocol, - (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, - this.maxConcurrentConnectionOpenRequests, - this.ConnectionPolicy.UserAgentContainer, - this.eventSource, - null, - this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.timerPoolGranularityInSeconds, - this.maxRntbdChannels, - this.rntbdPartitionCount, - this.maxRequestsPerRntbdChannel, - (Documents.PortReuseMode)this.rntbdPortReuseMode, - this.rntbdPortPoolReuseThreshold, - this.rntbdPortPoolBindAttempts, - receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, - sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, - retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), - enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, - remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, - distributedTracingOptions: distributedTracingOptions, - enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), - chaosInterceptor: this.chaosInterceptor); - - if (this.transportClientHandlerFactory != null) - { - newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); - } - - this.storeClientFactory = newClientFactory; - this.isStoreClientFactoryCreatedInternally = true; + this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); + + this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( + this.ApiType, + DocumentClientEventSource.Instance, + this.ConnectionPolicy, + handler, + this.sendingRequest, + this.receivedResponse, + this.chaosInterceptor); + + // Loading VM Information (non blocking call and initialization won't fail if this call fails) + VmMetadataApiHandler.TryInitialize(this.httpClient); + + if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) + { + CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); + CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); + + CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); + } + + // Starting ClientTelemetry Job + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, + this.ConnectionPolicy, + this.cosmosAuthorization, + this.httpClient, + this.ServiceEndpoint, + this.GlobalEndpointManager, + this.cancellationTokenSource, + this.chaosInterceptor is not null); + + if (sessionContainer != null) + { + this.sessionContainer = sessionContainer; + } + else + { + this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); + } + + this.desiredConsistencyLevel = desiredConsistencyLevel; + // Setup the proxy to be used based on connection mode. + // For gateway: GatewayProxy. + // For direct: WFStoreProxy [set in OpenAsync()]. + this.eventSource = DocumentClientEventSource.Instance; + + this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( + () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), + new ResourceThrottleRetryPolicy( + this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, + this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); + + // Create the task to start the initialize task + // Task will be awaited on in the EnsureValidClientAsync + Task initTask = this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + + // ContinueWith on the initialization task is needed for handling the UnobservedTaskException + // if this task throws for some reason. Awaiting inside a constructor is not supported and + // even if we had to await inside GetInitializationTask to catch the exception, that will + // be a blocking call. In such cases, the recommended approach is to "handle" the + // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted + // and accessing the Exception property on the target task. +#pragma warning disable VSTHRD110 // Observe result of async calls +#pragma warning disable CDX1000 // DontConvertExceptionToObject + initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); +#pragma warning restore CDX1000 // DontConvertExceptionToObject +#pragma warning restore VSTHRD110 // Observe result of async calls + + this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); + DefaultTrace.TraceInformation(string.Format( + CultureInfo.InvariantCulture, + "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", + this.traceId, + serviceEndpoint.ToString(), + this.ConnectionPolicy.ConnectionMode.ToString(), + this.ConnectionPolicy.ConnectionProtocol.ToString(), + desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); + + this.QueryCompatibilityMode = QueryCompatibilityMode.Default; + } + + // Always called from under the lock except when called from Intilialize method during construction. + private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) + { + await this.InitializeGatewayConfigurationReaderAsync(); + + if (this.desiredConsistencyLevel.HasValue) + { + this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); } - this.AddressResolver = new GlobalAddressResolver( - this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.ConnectionPolicy.ConnectionProtocol, - this, - this.collectionCache, - this.partitionKeyRangeCache, - this.accountServiceConfiguration, - this.ConnectionPolicy, - this.httpClient, - this.storeClientFactory.GetConnectionStateListener(), - this.enableAsyncCacheExceptionNoSharing); - - this.CreateStoreModel(subscribeRntbdStatus: true); - } - - private void CreateStoreModel(bool subscribeRntbdStatus) - { - //EnableReadRequestsFallback, if not explicity set on the connection policy, - //is false if the account's consistency is bounded staleness, - //and true otherwise. - StoreClient storeClient = this.storeClientFactory.CreateStoreClient( - this.AddressResolver, - this.sessionContainer, - this.accountServiceConfiguration, - this, - true, - this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), - !this.enableRntbdChannel, - this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), - true, - enableReplicaValidation: this.isReplicaAddressValidationEnabled, - sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); - - if (subscribeRntbdStatus) + if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride + && this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue) { - storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); + this.ConnectionPolicy.EnablePartitionLevelFailover = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value; } - storeClient.SerializerSettings = this.serializerSettings; - - this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); - } - - private void DisableRntbdChannel() - { - Debug.Assert(this.enableRntbdChannel); - this.enableRntbdChannel = false; - this.CreateStoreModel(subscribeRntbdStatus: false); - } + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover; + this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures()); + this.InitializePartitionLevelFailoverWithDefaultHedging(); - private async Task InitializeGatewayConfigurationReaderAsync() - { - GatewayAccountReader accountReader = new GatewayAccountReader( - serviceEndpoint: this.ServiceEndpoint, - cosmosAuthorization: this.cosmosAuthorization, - connectionPolicy: this.ConnectionPolicy, - httpClient: this.httpClient, - cancellationToken: this.cancellationTokenSource.Token, - isThinClientEnabled: this.isThinClientEnabled); + this.PartitionKeyRangeLocation = + this.ConnectionPolicy.EnablePartitionLevelFailover + || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker + ? new GlobalPartitionEndpointManagerCore( + this.GlobalEndpointManager, + this.ConnectionPolicy.EnablePartitionLevelFailover, + this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) + : GlobalPartitionEndpointManagerNoOp.Instance; - this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); + this.retryPolicy = new RetryPolicy( + globalEndpointManager: this.GlobalEndpointManager, + connectionPolicy: this.ConnectionPolicy, + partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation); + + this.ResetSessionTokenRetryPolicy = this.retryPolicy; + + GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( + this.GlobalEndpointManager, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + this.PartitionKeyRangeLocation, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + + this.GatewayStoreModel = gatewayStoreModel; + + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); + + gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway && this.isThinClientEnabled) + { + ThinClientStoreModel thinClientStoreModel = new ( + endpointManager: this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover ||this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, + chaosInterceptor: this.chaosInterceptor); + + thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); + + this.StoreModel = thinClientStoreModel; + } + else if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) + { + this.StoreModel = this.GatewayStoreModel; + } + else + { + this.InitializeDirectConnectivity(storeClientFactory); + } + + return true; + } + + private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) + { + if (databaseName == null) + { + throw new ArgumentNullException(nameof(databaseName)); + } + + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + using ( + DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Query, + ResourceType.Document, + collection.SelfLink, + AuthorizationTokenType.PrimaryMasterKey)) + { + ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( + resolvedCollection.ResourceId, + new Range( + PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, + PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, + true, + false), + NoOpTrace.Singleton); + + // In Gateway mode, AddressCache is null + if (this.AddressResolver != null) + { + await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); + } + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. + /// + /// + /// + /// The session object used for version tracking when the consistency level is set to Session. + /// + /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. + /// + public object Session + { + get + { + return this.sessionContainer; + } + + set + { + SessionContainer container = value as SessionContainer; + if (container == null) + { + throw new ArgumentNullException("value"); + } + + if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + ClientResources.BadSession, + container.HostName, + this.ServiceEndpoint.Host)); + } + + SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; + if (currentSessionContainer == null) + { + throw new ArgumentNullException(nameof(currentSessionContainer)); + } + + currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. + /// + /// Collection for which session token must be retrieved. + /// + /// The session token used for version tracking when the consistency level is set to Session. + /// + /// + /// The session token can be saved and supplied to a request via . + /// + internal string GetSessionToken(string collectionLink) + { + SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; + + if (sessionContainerInternal == null) + { + throw new ArgumentNullException(nameof(sessionContainerInternal)); + } + + return sessionContainerInternal.GetSessionToken(collectionLink); + } + + /// + /// Gets the Api type + /// + internal ApiType ApiType + { + get; private set; + } + + internal bool UseMultipleWriteLocations { get; private set; } + + /// + /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. + /// + /// + /// The Uri for the service endpoint. + /// + /// + public Uri ServiceEndpoint + { + get; + private set; + } + + /// + /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri WriteEndpoint + { + get + { + return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri ReadEndpoint + { + get + { + return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the Connection policy used by the client from the Azure Cosmos DB service. + /// + /// + /// The Connection policy used by the client. + /// + /// + public ConnectionPolicy ConnectionPolicy { get; private set; } + + /// + /// Gets the AuthKey used by the client from the Azure Cosmos DB service. + /// + /// + /// The AuthKey used by the client. + /// + /// + public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); + + /// + /// Gets the configured consistency level of the client from the Azure Cosmos DB service. + /// + /// + /// The configured of the client. + /// + /// + public virtual Documents.ConsistencyLevel ConsistencyLevel + { + get + { +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : + this.accountServiceConfiguration.DefaultConsistencyLevel; + } + } + + /// + /// Returns the account properties available in the service configuration if the client was initialized. + /// + public bool TryGetCachedAccountProperties(out AccountProperties properties) + { + if (this.isSuccessfullyInitialized + && this.accountServiceConfiguration != null + && this.accountServiceConfiguration.AccountProperties != null) + { + properties = this.accountServiceConfiguration.AccountProperties; + return true; + } + + properties = null; + return false; + } + + /// + /// Disposes the client for the Azure Cosmos DB service. + /// + /// + /// + /// + /// + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + if (this.telemetryToServiceHelper != null) + { + this.telemetryToServiceHelper.Dispose(); + this.telemetryToServiceHelper = null; + } + + if (!this.cancellationTokenSource.IsCancellationRequested) + { + this.cancellationTokenSource.Cancel(); + } + + this.cancellationTokenSource.Dispose(); + + if (this.StoreModel != null) + { + this.StoreModel.Dispose(); + this.StoreModel = null; + } + + if (this.storeClientFactory != null) + { + // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference + if (this.isStoreClientFactoryCreatedInternally) + { + this.storeClientFactory.Dispose(); + } + + this.storeClientFactory = null; + } + + if (this.AddressResolver != null) + { + this.AddressResolver.Dispose(); + this.AddressResolver = null; + } + + if (this.httpClient != null) + { + try + { + this.httpClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.httpClient = null; + } + + if (this.cosmosAuthorization != null) + { + this.cosmosAuthorization.Dispose(); + } + + if (this.GlobalEndpointManager != null) + { + this.GlobalEndpointManager.Dispose(); + this.GlobalEndpointManager = null; + } + + if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) + { + this.queryPartitionProvider.Value.Dispose(); + } + + if (this.initTaskCache != null) + { + this.initTaskCache.Dispose(); + this.initTaskCache = null; + } + + DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); + DefaultTrace.Flush(); + + this.isDisposed = true; + } + + //Compatibility mode: + // Allows to specify compatibility mode used by client when making query requests. + // should be removed when application/sql is no longer supported. + internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } + + /// + /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). + /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache + /// to resolves the request's collection name. If it differs from the session container's resource id it + /// explains the session unavailable exception: somebody removed and recreated the collection. In this + /// case we retry once again (with empty session token) otherwise we return the error to the client + /// (see RenameCollectionAwareClientRetryPolicy) + /// + internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } + + /// + /// Gets and sets the IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension StoreModel { get; set; } + + /// + /// Gets and sets the gateway IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension GatewayStoreModel { get; set; } + + /// + /// Gets and sets on execute scalar query callback + /// + /// + /// Test hook to enable unit test for scalar queries + /// + internal Action OnExecuteScalarQueryCallback { get; set; } + + internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; + + internal virtual async Task GetDefaultConsistencyLevelAsync() + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; + } + + internal Task GetDesiredConsistencyLevelAsync() + { + return Task.FromResult(this.desiredConsistencyLevel); + } + + internal async Task ProcessRequestAsync( + string verb, + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + string testAuthorization = null) // Only for unit-tests + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (verb == null) + { + throw new ArgumentNullException(nameof(verb)); + } + + (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( + request.ResourceAddress, + PathsHelper.GetResourcePath(request.ResourceType), + verb, + request.Headers, + AuthorizationTokenType.PrimaryMasterKey); + + // Unit-test hook + if (testAuthorization != null) + { + payload = testAuthorization; + authorization = testAuthorization; + } + request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; + + try + { + return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); + } + catch (DocumentClientException dce) + { + this.cosmosAuthorization.TraceUnauthorized( + dce, + authorization, + payload); + + throw; + } + } + + internal Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); + } + + internal async Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + ITrace trace, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(trace); + + retryPolicyInstance?.OnBeforeSendRequest(request); + + using (new ActivityScope(Guid.NewGuid())) + { + IStoreModel storeProxy = this.GetStoreProxy(request); + return await storeProxy.ProcessMessageAsync(request, cancellationToken); + } + } + + /// + /// Establishes and Initializes the Rntbd connection to all the backend replica nodes + /// for the given database name and container. + /// + /// A string containing the cosmos database name. + /// A string containing the cosmos container link uri. + /// An instance of the . + internal async Task OpenConnectionsToAllReplicasAsync( + string databaseName, + string containerLinkUri, + CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(databaseName) || + string.IsNullOrEmpty(containerLinkUri)) + { + string resourceName = string.IsNullOrEmpty(databaseName) ? + nameof(databaseName) : + nameof(containerLinkUri); + + throw new ArgumentNullException(resourceName); + } + + if (this.StoreModel != null) + { + try + { + await this.StoreModel.OpenConnectionsToAllReplicasAsync( + databaseName, + containerLinkUri, + cancellationToken); + } + catch (Exception) + { + throw; + } + } + } + + private static string NormalizeAuthorizationPayload(string input) + { + const int expansionBuffer = 12; + StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); + for (int i = 0; i < input.Length; i++) + { + switch (input[i]) + { + case '\n': + builder.Append("\\n"); + break; + case '/': + builder.Append("\\/"); + break; + default: + builder.Append(input[i]); + break; + } + } + + return builder.ToString(); + } + + internal async Task InitilizeFaultInjectionAsync() + { + if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) + { + this.isChaosInterceptorInititalized = true; + await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); + } + } + + internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) + { + return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.maxRequestsPerRntbdChannel, + this.maxRntbdChannels, + this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + this.rntbdPortReuseMode); + } + + internal virtual async Task EnsureValidClientAsync(ITrace trace) + { + if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) + { + return; + } + + // Trace when the Initialization of client has not been completed. Usually during first call + using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) + { + // If the initialization task failed, we should retry initialization. + // We may end up throwing the same exception but this will ensure that we dont have a + // client which is unusable and can resume working if it failed initialization once. + // If we have to reinitialize the client, it needs to happen in thread safe manner so that + // we dont re-initalize the task again for each incoming call. + try + { + this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + } + catch (DocumentClientException ex) + { + throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( + dce: ex, + trace: trace); + } + catch (Exception e) + { + DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); + childTrace.AddDatum("initializeTask failed", e.Message); + throw; + } + + await this.InitilizeFaultInjectionAsync(); + } + } + + #region Create Impl + /// + /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed. + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); + } + + private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, + Documents.Client.RequestOptions options) + { + if (database == null) + { + throw new ArgumentNullException("database"); + } + + // Doing a Read before Create will give us better latency for existing databases + try + { + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDatabaseAsync(database, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + + /// + /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to create. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// Finally, a Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Create a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, + object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, + CancellationToken cancellationToken = default) + { + // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy); + } + + private async Task> CreateDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 collection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDocumentCollectionPrivateAsync( + string databaseLink, + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse( + await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 DocumentCollection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); + } + + private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( + string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) + { + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make + // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. + Documents.Database database = await this.ReadDatabaseAsync(databaseLink); + + // Doing a Read before Create will give us better latency for existing collections. + // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, + // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. + try + { + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + + /// + /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link to the source object. + /// The target object. + /// (optional)The point in time to restore. If null, use the latest restorable time. + /// (Optional) The for the request. + /// The task object representing the service response for the asynchronous operation. + internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + if (targetDocumentCollection == null) + { + throw new ArgumentNullException("targetDocumentCollection"); + } + + bool isFeed; + string resourceTypeString; + string resourceIdOrFullName; + bool isNameBased; + + string dbsId; + string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); + if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + dbsId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + string sourceCollId; + if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + sourceCollId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + this.ValidateResource(targetDocumentCollection); + + if (options == null) + { + options = new Documents.Client.RequestOptions(); + } + if (!options.RemoteStorageType.HasValue) + { + options.RemoteStorageType = RemoteStorageType.Standard; + } + options.SourceDatabaseId = dbsId; + options.SourceCollectionId = sourceCollId; + if (restoreTime.HasValue) + { + options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + targetDocumentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Get the status of a collection being restored in the Azure Cosmos DB service. + /// + /// The link of the document collection being restored. + /// The task object representing the service response for the asynchronous operation. + internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (string.IsNullOrEmpty(targetDocumentCollectionLink)) + { + throw new ArgumentNullException("targetDocumentCollectionLink"); + } + + ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( + targetDocumentCollectionLink, + new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, + retryPolicyInstance); + string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); + if (restoreState == null) + { + restoreState = RestoreState.RestoreCompleted.ToString(); + } + + DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() + { + State = restoreState + }; + + return ret; + } + + /// + /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ + /// The object to create. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the created object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. + /// + /// + /// 409 + /// + /// Conflict - This means a with an id matching the id field of already existed, + /// or there is already a pending snapshot for the specified resource link. + /// + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that + /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (snapshot == null) + { + throw new ArgumentNullException("snapshot"); + } + + this.ValidateResource(snapshot); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Snapshots_Root, + snapshot, + ResourceType.Snapshot, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Delete Impl + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. snapshots/snapshot_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Replace Impl + /// + /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. + /// + /// the updated document collection. + /// the request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceDocumentCollectionPrivateAsync( + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(documentCollection), + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + // set the session token + if (collection.Resource != null) + { + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + } + return collection; + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If either or is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now dynamically cast doc back to your MyPoco + /// MyPoco poco = (dynamic)doc; + /// + /// //Update some properties of the poco object + /// poco.MyProperty = "updated value"; + /// + /// //Now persist these changes to the database using doc.SelLink and the update poco object + /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if ((options == null) || (options.PartitionKey == null)) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible( + () => this.ReplaceDocumentPrivateAsync( + documentLink, + document, + options, + requestRetryPolicy, + cancellationToken), + requestRetryPolicy, + cancellationToken); + } + + private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// doc.SetPropertyValue("MyProperty", "updated value"); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Document updated = await client.ReplaceDocumentAsync(doc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( + this.GetLinkForRouting(document), + document, + options, + retryPolicyInstance, + cancellationToken), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + this.ValidateResource(document); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + documentLink, + document, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, document, options); + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "sproc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// sproc.Body = "function () {new javascript body for sproc}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceStoredProcedurePrivateAsync( + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(storedProcedure), + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "trigger id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// trigger.Body = "function () {new javascript body for trigger}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// Trigger updated = await client.ReplaceTriggerAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(trigger), + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "udf id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// udf.Body = "function () {new javascript body for udf}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedFunctionPrivateAsync( + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(function), + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. + /// + /// + /// + /// + /// + /// r.ResourceLink == "collection selfLink") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Create a new offer with the changed throughput + /// OfferV2 newOffer = new OfferV2(offer, 5000); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Offer updated = await client.ReplaceOfferAsync(newOffer); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceOfferAsync(Offer offer) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (offer == null) + { + throw new ArgumentNullException("offer"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + offer.SelfLink, + offer, + ResourceType.Offer, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse( + await this.UpdateAsync(request, retryPolicyInstance), + OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "user defined type id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(userDefinedType), + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Read Impl + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Database resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// (docLink); + /// ]]> + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the DocumentCollection to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionPrivateAsync( + string documentCollectionLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the stored procedure to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Trigger to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the User Defined Function to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Conflict to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads an from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Offer to be read. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. + /// + /// + /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about + /// minimum throughput of a Cosmos container (or a database) + /// To retrieve the minimum throughput for a collection/database, use the following sample + /// + /// response = await client.ReadOfferAsync(offer.SelfLink); + /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; + /// ]]> + /// + /// + /// + /// + /// + /// + /// + /// + public Task> ReadOfferAsync(string offerLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(offerLink)) + { + throw new ArgumentNullException("offerLink"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Offer, + offerLink, + null, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Reads a as an asynchronous operation. + /// + /// The link for the schema to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Schema are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentSchemaLink)) + { + throw new ArgumentNullException("documentSchemaLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Schema, + documentSchemaLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the UserDefinedType resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypeLink)) + { + throw new ArgumentNullException("userDefinedTypeLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedType, + userDefinedTypeLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Snapshot resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Snapshot are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/snapshots/{snapshot identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region ReadFeed Impl + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDatabaseFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = null; + /// List ids = new List(); + /// do + /// { + /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); + /// foreach (var item in response) + /// { + /// ids.Add(item.Id); + /// } + /// } + /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(partitionKeyRangesLink)) + { + throw new ArgumentNullException("partitionKeyRangesLink"); + } + + return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionsLink)) + { + throw new ArgumentNullException("collectionsLink"); + } + + return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProceduresLink)) + { + throw new ArgumentNullException("storedProceduresLink"); + } + + return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggersLink)) + { + throw new ArgumentNullException("triggersLink"); + } + + return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedFunctionsLink)) + { + throw new ArgumentNullException("userDefinedFunctionsLink"); + } + + return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. + /// This takes returns a which will contain an enumerable list of dynamic objects. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a containing dynamic objects representing the items in the feed. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. + /// This is important becuse a DocumentCollection can contain different kinds of documents. + /// + /// + /// + /// + public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentsLink)) + { + throw new ArgumentNullException("documentsLink"); + } + + DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); + return new DocumentFeedResponse( + response.Cast(), + response.Count, + response.Headers, + response.UseETagAsContinuation, + response.QueryMetrics, + response.RequestStatistics, + responseLengthBytes: response.ResponseLengthBytes); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); + } + + private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictsLink)) + { + throw new ArgumentNullException("conflictsLink"); + } + + return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service + /// as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadOfferAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadOffersFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionSchemaLink)) + { + throw new ArgumentNullException("documentCollectionSchemaLink"); + } + + return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypesLink)) + { + throw new ArgumentNullException("userDefinedTypesLink"); + } + + return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a set of containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadSnapshotFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); + } + + #endregion + + #region Stored procs + /// + /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) + { + return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + default, + procedureParams), + retryPolicyInstance); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + cancellationToken, + procedureParams), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ExecuteStoredProcedurePrivateAsync( + string storedProcedureLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + params dynamic[] procedureParams) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); + string storedProcedureInput = serializerSettings == null ? + JsonConvert.SerializeObject(procedureParams) : + JsonConvert.SerializeObject(procedureParams, serializerSettings); + using (MemoryStream storedProcedureInputStream = new MemoryStream()) + { + using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) + { + await writer.WriteAsync(storedProcedureInput); + await writer.FlushAsync(); + storedProcedureInputStream.Position = 0; + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.ExecuteJavaScript, + ResourceType.StoredProcedure, + storedProcedureLink, + storedProcedureInputStream, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); + if (options?.PartitionKeyRangeId == null) + { + await this.AddPartitionKeyInformationAsync( + request, + options); + } + + retryPolicyInstance?.OnBeforeSendRequest(request); + + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new StoredProcedureResponse(await this.ExecuteProcedureAsync( + request, + retryPolicyInstance, + cancellationToken), + this.GetSerializerSettingsForRequest(options)); + } + } + } + } + + #endregion + + #region Upsert Impl + /// + /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to upsert. + /// (Optional) The for the request. + /// The that was upserted within a task object representing the service response for the asynchronous operation. + /// If is not set + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// + /// + /// The example below upserts a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to upsert. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// A Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Upsert a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) + { + // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy, cancellationToken); + } + + private async Task> UpsertDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + // To be implemented. + throw new NotImplementedException(); + } + + /// + /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. + /// + /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ + /// The object to upsert. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the upserted object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + #endregion + + #region IAuthorizationTokenProvider + + ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType) + { + return this.cosmosAuthorization.GetUserAuthorizationAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType); + } + + ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType, + ITrace trace) + { + return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType, + trace); + } + + Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( + DocumentServiceRequest request, + string federationId, + string verb, + string resourceId) + { + return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( + request, + federationId, + verb, + resourceId); + } + + #endregion + + #region Core Implementation + internal Task CreateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpdateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); + } + + internal Task ReadAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task ReadFeedAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task DeleteAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteProcedureAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteQueryAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpsertAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + #endregion + + /// + /// Read the from the Azure Cosmos DB service as an asynchronous operation. + /// + /// + /// A wrapped in a object. + /// + public Task GetDatabaseAccountAsync() + { + return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); + } + + /// + /// Read the as an asynchronous operation + /// given a specific reginal endpoint url. + /// + /// The reginal url of the serice endpoint. + /// The CancellationToken + /// + /// A wrapped in a object. + /// + Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) + { + return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); + } + + private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) + { + async ValueTask CreateRequestMessage() + { + HttpRequestMessage request = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = serviceEndpoint + }; + + INameValueCollection headersCollection = new StoreResponseNameValueCollection(); + await this.cosmosAuthorization.AddAuthorizationHeaderAsync( + headersCollection, + serviceEndpoint, + "GET", + AuthorizationTokenType.PrimaryMasterKey); + + foreach (string key in headersCollection.AllKeys()) + { + request.Headers.Add(key, headersCollection[key]); + } + + return request; + } + + AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, + clientSideRequestStatistics: null); + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; + + if (this.queryPartitionProvider.IsValueCreated) + { + (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); + } + + return databaseAccount; + } + + return null; + } + + #region Private Impl + + /// + /// Certain requests must be routed through gateway even when the client connectivity mode is direct. + /// For e.g., DocumentCollection creation. This method returns the based + /// on the input . + /// + /// Returns to which the request must be sent + internal IStoreModel GetStoreProxy(DocumentServiceRequest request) + { + // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) + // we return the Gateway store model + if (request.UseGatewayMode) + { + return this.GatewayStoreModel; + } + + ResourceType resourceType = request.ResourceType; + OperationType operationType = request.OperationType; + + if (resourceType == ResourceType.Offer || + (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || + resourceType == ResourceType.PartitionKeyRange || + resourceType == ResourceType.Snapshot || + resourceType == ResourceType.ClientEncryptionKey || + (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) + { + return this.GatewayStoreModel; + } + + if (this.isThinClientEnabled + && operationType == OperationType.Read + && resourceType == ResourceType.Database) + { + return this.GatewayStoreModel; + } + + if (operationType == OperationType.Create + || operationType == OperationType.Upsert) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection || + resourceType == ResourceType.Permission) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Delete) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Read) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else + { + return this.StoreModel; + } + } + + /// + /// The preferred link used in replace operation in SDK. + /// + private string GetLinkForRouting(Documents.Resource resource) + { + // we currently prefer the selflink + return resource.SelfLink ?? resource.AltLink; + } + + internal void EnsureValidOverwrite( + Documents.ConsistencyLevel desiredConsistencyLevel, + OperationType? operationType = null, + ResourceType? resourceType = null) + { + Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; + if (!this.IsValidConsistency( + defaultConsistencyLevel, + desiredConsistencyLevel, + operationType, + resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + desiredConsistencyLevel.ToString(), + defaultConsistencyLevel.ToString())); + } + } + + private bool IsValidConsistency( + Documents.ConsistencyLevel backendConsistency, + Documents.ConsistencyLevel desiredConsistency, + OperationType? operationType, + ResourceType? resourceType) + { + if (this.allowOverrideStrongerConsistency) + { + return true; + } + + return ValidationHelpers.IsValidConsistencyLevelOverwrite( + backendConsistency: backendConsistency, + desiredConsistency: desiredConsistency, + isLocalQuorumConsistency: this.IsLocalQuorumConsistency, + operationType: operationType, + resourceType: resourceType); + } + + private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) + { + // Check if we have a store client factory in input and if we do, do not initialize another store client + // The purpose is to reuse store client factory across all document clients inside compute gateway + if (storeClientFactory != null) + { + this.storeClientFactory = storeClientFactory; + this.isStoreClientFactoryCreatedInternally = false; + } + else + { + // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, + // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. + Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () + { +#if INTERNAL + IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing +#else + IsDistributedTracingEnabled = false +#endif + + }; + + StoreClientFactory newClientFactory = new StoreClientFactory( + this.ConnectionPolicy.ConnectionProtocol, + (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, + this.maxConcurrentConnectionOpenRequests, + this.ConnectionPolicy.UserAgentContainer, + this.eventSource, + null, + this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.timerPoolGranularityInSeconds, + this.maxRntbdChannels, + this.rntbdPartitionCount, + this.maxRequestsPerRntbdChannel, + (Documents.PortReuseMode)this.rntbdPortReuseMode, + this.rntbdPortPoolReuseThreshold, + this.rntbdPortPoolBindAttempts, + receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, + sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, + retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), + enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, + remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, + distributedTracingOptions: distributedTracingOptions, + enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), + chaosInterceptor: this.chaosInterceptor); + + if (this.transportClientHandlerFactory != null) + { + newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); + } + + this.storeClientFactory = newClientFactory; + this.isStoreClientFactoryCreatedInternally = true; + } + + this.AddressResolver = new GlobalAddressResolver( + this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.ConnectionPolicy.ConnectionProtocol, + this, + this.collectionCache, + this.partitionKeyRangeCache, + this.accountServiceConfiguration, + this.ConnectionPolicy, + this.httpClient, + this.storeClientFactory.GetConnectionStateListener(), + this.enableAsyncCacheExceptionNoSharing); + + this.CreateStoreModel(subscribeRntbdStatus: true); + } + + private void CreateStoreModel(bool subscribeRntbdStatus) + { + //EnableReadRequestsFallback, if not explicity set on the connection policy, + //is false if the account's consistency is bounded staleness, + //and true otherwise. + StoreClient storeClient = this.storeClientFactory.CreateStoreClient( + this.AddressResolver, + this.sessionContainer, + this.accountServiceConfiguration, + this, + true, + this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), + !this.enableRntbdChannel, + this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), + true, + enableReplicaValidation: this.isReplicaAddressValidationEnabled, + sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); + + if (subscribeRntbdStatus) + { + storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); + } + + storeClient.SerializerSettings = this.serializerSettings; + + this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); + } + + private void DisableRntbdChannel() + { + Debug.Assert(this.enableRntbdChannel); + this.enableRntbdChannel = false; + this.CreateStoreModel(subscribeRntbdStatus: false); + } + + private async Task InitializeGatewayConfigurationReaderAsync() + { + GatewayAccountReader accountReader = new GatewayAccountReader( + serviceEndpoint: this.ServiceEndpoint, + cosmosAuthorization: this.cosmosAuthorization, + connectionPolicy: this.ConnectionPolicy, + httpClient: this.httpClient, + cancellationToken: this.cancellationTokenSource.Token, + isThinClientEnabled: this.isThinClientEnabled); + + this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); + await this.accountServiceConfiguration.InitializeAsync(); AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; - this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); - } + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; + this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); + } internal string GetUserAgentFeatures() { @@ -6888,379 +6888,379 @@ internal void InitializePartitionLevelFailoverWithDefaultHedging() thresholdStep: TimeSpan.FromMilliseconds(DocumentClient.DefaultHedgingThresholdStepInMilliseconds)); } } - - internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) - { - this.sessionContainer.SetSessionToken(request, response.Headers); - } - - internal DocumentServiceRequest CreateDocumentServiceRequest( - OperationType operationType, - string resourceLink, - ResourceType resourceType, - INameValueCollection headers) - { - if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) - { - return DocumentServiceRequest.Create( - operationType, - null, - resourceType, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - else - { - return DocumentServiceRequest.Create( - operationType, - resourceType, - resourceLink, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - } - - internal void ValidateResource(Documents.Resource resource) - { - this.ValidateResource(resource.Id); - } - - internal void ValidateResource(string resourceId) - { - if (!string.IsNullOrEmpty(resourceId)) - { - int match = resourceId.IndexOfAny(resourceIdSeparators); - if (match != -1) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidCharacterInResourceName, - resourceId[match])); - } - - if (resourceId[resourceId.Length - 1] == ' ') - { - throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); - } - } - } - - private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - PartitionKeyInternal partitionKey; - if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else if (options?.PartitionKey != null) - { - partitionKey = options.PartitionKey.InternalKey; - } - else - { - partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - // For backward compatibility, if collection doesn't have partition key defined, we assume all documents - // have empty value for it and user doesn't need to specify it explicitly. - PartitionKeyInternal partitionKey; - if (options?.PartitionKey == null) - { - if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) - { - partitionKey = PartitionKeyInternal.Empty; - } - else - { - throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); - } - } - else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else - { - partitionKey = options.PartitionKey.InternalKey; - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) - { - return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; - } - - private INameValueCollection GetRequestHeaders( - Documents.Client.RequestOptions options, - OperationType operationType, - ResourceType resourceType) - { - Debug.Assert( - this.isSuccessfullyInitialized, - "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); - - RequestNameValueCollection headers = new RequestNameValueCollection(); - - if (this.UseMultipleWriteLocations) - { - headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); - } - - if (this.desiredConsistencyLevel.HasValue) - { - // check anyways since default consistency level might have been refreshed. - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: this.desiredConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); - } - - if (options == null) - { - return headers; - } - - if (options.AccessCondition != null) - { - if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) - { - headers.IfMatch = options.AccessCondition.Condition; - } - else - { - headers.IfNoneMatch = options.AccessCondition.Condition; - } - } - - if (options.ConsistencyLevel.HasValue) - { - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: options.ConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); - } - - if (options.PriorityLevel.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); - } - - if (options.IndexingDirective.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); - } - - if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) - { - string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); - } - - if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) - { - string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); - } - - if (!string.IsNullOrEmpty(options.SessionToken)) - { - headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; - } - - if (options.ResourceTokenExpirySeconds.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferType != null) - { - headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); - } - - if (options.OfferThroughput.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferEnableRUPerMinuteThroughput) - { - headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); - } - - if (options.InsertSystemPartitionKey) - { - headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); - } - - //if (options.OfferAutopilotTier.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); - //} - - //if (options.OfferAutopilotAutoUpgrade.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); - //} - - if (options.EnableScriptLogging) - { - headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); - } - - if (options.PopulateQuotaInfo) - { - headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); - } - - if (options.PopulateRestoreStatus) - { - headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); - } - - if (options.PopulatePartitionKeyRangeStatistics) - { - headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); - } - - if (options.DisableRUPerMinuteUsage) - { - headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); - } - - if (options.RemoteStorageType.HasValue) - { - headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); - } - - if (options.PartitionKeyRangeId != null) - { - headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); - } - - if (options.SourceDatabaseId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); - } - - if (options.SourceCollectionId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); - } - - if (options.RestorePointInTime.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.IsReadOnlyScript) - { - headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); - } - - if (options.IncludeSnapshotDirectories) - { - headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); - } - - if (options.ExcludeSystemProperties.HasValue) - { - headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); - } - - if (options.MergeStaticId != null) - { - headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); - } - - if (options.PreserveFullContent) - { - headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); - } - - if (options.ThroughputBucket.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); - } - - return headers; - } - - private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory - { - private readonly IRetryPolicyFactory retryPolicy; - private readonly ISessionContainer sessionContainer; - private readonly ClientCollectionCache collectionCache; - - public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) - { - this.retryPolicy = retryPolicy; - this.sessionContainer = sessionContainer; - this.collectionCache = collectionCache; - } - - public IDocumentClientRetryPolicy GetRequestPolicy() - { - return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); - } - } - - private class HttpRequestMessageHandler : DelegatingHandler - { - private readonly EventHandler sendingRequest; - private readonly EventHandler receivedResponse; - - public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) - { - this.sendingRequest = sendingRequest; - this.receivedResponse = receivedResponse; - - this.InnerHandler = innerHandler ?? new HttpClientHandler(); - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); - HttpResponseMessage response = await base.SendAsync(request, cancellationToken); - this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); - return response; - } - } - - #endregion - } -} + + internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) + { + this.sessionContainer.SetSessionToken(request, response.Headers); + } + + internal DocumentServiceRequest CreateDocumentServiceRequest( + OperationType operationType, + string resourceLink, + ResourceType resourceType, + INameValueCollection headers) + { + if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) + { + return DocumentServiceRequest.Create( + operationType, + null, + resourceType, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + else + { + return DocumentServiceRequest.Create( + operationType, + resourceType, + resourceLink, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + } + + internal void ValidateResource(Documents.Resource resource) + { + this.ValidateResource(resource.Id); + } + + internal void ValidateResource(string resourceId) + { + if (!string.IsNullOrEmpty(resourceId)) + { + int match = resourceId.IndexOfAny(resourceIdSeparators); + if (match != -1) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidCharacterInResourceName, + resourceId[match])); + } + + if (resourceId[resourceId.Length - 1] == ' ') + { + throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); + } + } + } + + private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + PartitionKeyInternal partitionKey; + if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else if (options?.PartitionKey != null) + { + partitionKey = options.PartitionKey.InternalKey; + } + else + { + partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + // For backward compatibility, if collection doesn't have partition key defined, we assume all documents + // have empty value for it and user doesn't need to specify it explicitly. + PartitionKeyInternal partitionKey; + if (options?.PartitionKey == null) + { + if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) + { + partitionKey = PartitionKeyInternal.Empty; + } + else + { + throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); + } + } + else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else + { + partitionKey = options.PartitionKey.InternalKey; + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) + { + return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; + } + + private INameValueCollection GetRequestHeaders( + Documents.Client.RequestOptions options, + OperationType operationType, + ResourceType resourceType) + { + Debug.Assert( + this.isSuccessfullyInitialized, + "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); + + RequestNameValueCollection headers = new RequestNameValueCollection(); + + if (this.UseMultipleWriteLocations) + { + headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); + } + + if (this.desiredConsistencyLevel.HasValue) + { + // check anyways since default consistency level might have been refreshed. + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: this.desiredConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); + } + + if (options == null) + { + return headers; + } + + if (options.AccessCondition != null) + { + if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) + { + headers.IfMatch = options.AccessCondition.Condition; + } + else + { + headers.IfNoneMatch = options.AccessCondition.Condition; + } + } + + if (options.ConsistencyLevel.HasValue) + { + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: options.ConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); + } + + if (options.PriorityLevel.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); + } + + if (options.IndexingDirective.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); + } + + if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) + { + string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); + } + + if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) + { + string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); + } + + if (!string.IsNullOrEmpty(options.SessionToken)) + { + headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; + } + + if (options.ResourceTokenExpirySeconds.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferType != null) + { + headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); + } + + if (options.OfferThroughput.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferEnableRUPerMinuteThroughput) + { + headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); + } + + if (options.InsertSystemPartitionKey) + { + headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); + } + + //if (options.OfferAutopilotTier.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); + //} + + //if (options.OfferAutopilotAutoUpgrade.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); + //} + + if (options.EnableScriptLogging) + { + headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); + } + + if (options.PopulateQuotaInfo) + { + headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); + } + + if (options.PopulateRestoreStatus) + { + headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); + } + + if (options.PopulatePartitionKeyRangeStatistics) + { + headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); + } + + if (options.DisableRUPerMinuteUsage) + { + headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); + } + + if (options.RemoteStorageType.HasValue) + { + headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); + } + + if (options.PartitionKeyRangeId != null) + { + headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); + } + + if (options.SourceDatabaseId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); + } + + if (options.SourceCollectionId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); + } + + if (options.RestorePointInTime.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.IsReadOnlyScript) + { + headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); + } + + if (options.IncludeSnapshotDirectories) + { + headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); + } + + if (options.ExcludeSystemProperties.HasValue) + { + headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); + } + + if (options.MergeStaticId != null) + { + headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); + } + + if (options.PreserveFullContent) + { + headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); + } + + if (options.ThroughputBucket.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); + } + + return headers; + } + + private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory + { + private readonly IRetryPolicyFactory retryPolicy; + private readonly ISessionContainer sessionContainer; + private readonly ClientCollectionCache collectionCache; + + public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) + { + this.retryPolicy = retryPolicy; + this.sessionContainer = sessionContainer; + this.collectionCache = collectionCache; + } + + public IDocumentClientRetryPolicy GetRequestPolicy() + { + return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); + } + } + + private class HttpRequestMessageHandler : DelegatingHandler + { + private readonly EventHandler sendingRequest; + private readonly EventHandler receivedResponse; + + public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) + { + this.sendingRequest = sendingRequest; + this.receivedResponse = receivedResponse; + + this.InnerHandler = innerHandler ?? new HttpClientHandler(); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); + HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); + return response; + } + } + + #endregion + } +} From f9ae96f9616f4dee4e9fcef24288085f08c0d53d Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:06:05 -0700 Subject: [PATCH 10/20] Update DocumentClient.cs --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index b2add943f7..aa94a7000c 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1116,7 +1116,7 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli this.eventSource, this.serializerSettings, this.httpClient, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover ||this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, chaosInterceptor: this.chaosInterceptor); thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); From 74f795eefa80a8fb9f19d34e769d5bf47922da96 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:04:39 -0700 Subject: [PATCH 11/20] added FI connection in GatewayStoreModel this will be needed for future work that is planned with moving the ThinClientStoreClient creation to the GatewayStoreModel --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 3 ++- Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index aa94a7000c..70bc8c6365 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1090,7 +1090,8 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli this.serializerSettings, this.httpClient, this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, + this.chaosInterceptor); this.GatewayStoreModel = gatewayStoreModel; diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 22a79eefc6..e39c87d439 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -19,6 +19,7 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.FaultInjection; using Newtonsoft.Json; // Marking it as non-sealed in order to unit test it using Moq framework @@ -32,6 +33,8 @@ internal class GatewayStoreModel : IStoreModelExtension, IDisposable private readonly DocumentClientEventSource eventSource; internal readonly ConsistencyLevel defaultConsistencyLevel; + private readonly IChaosInterceptor chaosInterceptor; + private GatewayStoreClient gatewayStoreClient; // Caches to resolve the PartitionKeyRange from request. For Session Token Optimization. @@ -47,7 +50,8 @@ public GatewayStoreModel( JsonSerializerSettings serializerSettings, CosmosHttpClient httpClient, GlobalPartitionEndpointManager globalPartitionEndpointManager, - bool isPartitionLevelFailoverEnabled = false) + bool isPartitionLevelFailoverEnabled = false, + IChaosInterceptor chaosInterceptor = null) { this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; this.endpointManager = endpointManager; @@ -63,6 +67,7 @@ public GatewayStoreModel( this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( this.MarkEndpointsToHealthyAsync); + this.chaosInterceptor = chaosInterceptor; } public virtual async Task ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default) From e4583675e77aa21fd478fafdafd88a2f0a9b81e2 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:52:28 -0700 Subject: [PATCH 12/20] Update ThinClientStoreClient.cs --- .../src/ThinClientStoreClient.cs | 332 +++++++++--------- 1 file changed, 166 insertions(+), 166 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index e8713046a7..760067c143 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -1,65 +1,65 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Concurrent; - using System.IO; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Routing; +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Concurrent; + using System.IO; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.FaultInjection; - using Newtonsoft.Json; - using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; - - /// - /// A TransportClient that sends requests to proxy endpoint. - /// And then processes the response back into DocumentServiceResponse objects. - /// - internal class ThinClientStoreClient : GatewayStoreClient - { + using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; + + /// + /// A TransportClient that sends requests to proxy endpoint. + /// And then processes the response back into DocumentServiceResponse objects. + /// + internal class ThinClientStoreClient : GatewayStoreClient + { + private readonly bool isPartitionLevelFailoverEnabled; private readonly ObjectPool bufferProviderWrapperPool; - private readonly bool isPartitionLevelFailoverEnabled; - private readonly IChaosInterceptor chaosInterceptor; - - public ThinClientStoreClient( - CosmosHttpClient httpClient, - ICommunicationEventSource eventSource, + private readonly IChaosInterceptor chaosInterceptor; + + public ThinClientStoreClient( + CosmosHttpClient httpClient, + ICommunicationEventSource eventSource, JsonSerializerSettings serializerSettings = null, bool isPartitionLevelFailoverEnabled = false, - IChaosInterceptor chaosInterceptor = null) - : base(httpClient, - eventSource, + IChaosInterceptor chaosInterceptor = null) + : base(httpClient, + eventSource, serializerSettings, - isPartitionLevelFailoverEnabled) - { - this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + isPartitionLevelFailoverEnabled) + { + this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; - this.chaosInterceptor = chaosInterceptor; - } - - public override async Task InvokeAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceType, - physicalAddress, - thinClientEndpoint, - globalDatabaseAccountName, - clientCollectionCache, - cancellationToken)) + this.chaosInterceptor = chaosInterceptor; + } + + public override async Task InvokeAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceType, + physicalAddress, + thinClientEndpoint, + globalDatabaseAccountName, + clientCollectionCache, + cancellationToken)) { if (this.chaosInterceptor != null) { @@ -72,43 +72,43 @@ public override async Task InvokeAsync( request.Headers.Remove("FAULTINJECTION_IS_PROXY"); return await ThinClientStoreClient.ParseResponseAsync(fiResponseMessage, request.SerializerSettings ?? base.SerializerSettings, request); } - } - HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); - return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); - } - } - - internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) - { - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? - HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : - HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); - - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceOperation.resourceType, - physicalAddress, - default, - default, - default, - default)) - { - return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); - } - } - - private async ValueTask PrepareRequestForProxyAsync( - DocumentServiceRequest request, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache) - { - HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; - requestMessage.Version = new Version(2, 0); - - BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); - try + } + HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); + return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); + } + } + + internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) + { + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? + HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : + HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); + + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceOperation.resourceType, + physicalAddress, + default, + default, + default, + default)) + { + return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); + } + } + + private async ValueTask PrepareRequestForProxyAsync( + DocumentServiceRequest request, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache) + { + HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; + requestMessage.Version = new Version(2, 0); + + BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); + try { PartitionKeyRange partitionKeyRange = request.RequestContext?.ResolvedPartitionKeyRange; @@ -122,80 +122,80 @@ private async ValueTask PrepareRequestForProxyAsync( ThinClientConstants.ProxyEndEpk, partitionKeyRange?.MaxExclusive); } - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyOperationType, - request.OperationType.ToOperationTypeString()); - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyResourceType, - request.ResourceType.ToResourceTypeString()); - - Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( - bufferProviderWrapper, - globalDatabaseAccountName, - clientCollectionCache, - requestMessage); - - if (!contentStream.CanSeek) - { - throw new InvalidOperationException( - $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); - } - - requestMessage.Content = new StreamContent(contentStream); - requestMessage.Content.Headers.ContentLength = contentStream.Length; + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyOperationType, + request.OperationType.ToOperationTypeString()); + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyResourceType, + request.ResourceType.ToResourceTypeString()); + + Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( + bufferProviderWrapper, + globalDatabaseAccountName, + clientCollectionCache, + requestMessage); + + if (!contentStream.CanSeek) + { + throw new InvalidOperationException( + $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); + } + + requestMessage.Content = new StreamContent(contentStream); + requestMessage.Content.Headers.ContentLength = contentStream.Length; - requestMessage.RequestUri = thinClientEndpoint; - requestMessage.Method = HttpMethod.Post; - - return requestMessage; - } - finally - { - this.bufferProviderWrapperPool.Return(bufferProviderWrapper); - } - } - - private Task InvokeClientAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); - return base.httpClient.SendHttpAsync( - () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), - resourceType, - HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), - request.RequestContext.ClientRequestStatistics, + requestMessage.RequestUri = thinClientEndpoint; + requestMessage.Method = HttpMethod.Post; + + return requestMessage; + } + finally + { + this.bufferProviderWrapperPool.Return(bufferProviderWrapper); + } + } + + private Task InvokeClientAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); + return base.httpClient.SendHttpAsync( + () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), + resourceType, + HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), + request.RequestContext.ClientRequestStatistics, cancellationToken, - request); - } - - internal class ObjectPool - { - private readonly ConcurrentBag Objects; - private readonly Func ObjectGenerator; - - public ObjectPool(Func objectGenerator) - { - this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); - this.Objects = new ConcurrentBag(); - } - - public T Get() - { - return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); - } - - public void Return(T item) - { - this.Objects.Add(item); - } - } - } + request); + } + + internal class ObjectPool + { + private readonly ConcurrentBag Objects; + private readonly Func ObjectGenerator; + + public ObjectPool(Func objectGenerator) + { + this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); + this.Objects = new ConcurrentBag(); + } + + public T Get() + { + return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); + } + + public void Return(T item) + { + this.Objects.Add(item); + } + } + } } \ No newline at end of file From fc1080e4d69af27a9af097d815a947f09472b858 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:31:33 -0700 Subject: [PATCH 13/20] fix merge --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 14361 ++++++++-------- .../src/ThinClientStoreClient.cs | 344 +- 2 files changed, 7354 insertions(+), 7351 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index 8244149c46..7b315b4116 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1,1055 +1,1055 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Security; - using System.Security; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using global::Azure.Core; - using Microsoft.Azure.Cosmos.Common; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Query; - using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; - using Microsoft.Azure.Cosmos.Routing; - using Microsoft.Azure.Cosmos.Telemetry; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tracing.TraceData; - using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Client; - using Microsoft.Azure.Documents.Collections; - using Microsoft.Azure.Documents.FaultInjection; - using Microsoft.Azure.Documents.Routing; - using Newtonsoft.Json; - using ResourceType = Documents.ResourceType; - - /// - /// Provides a client-side logical representation for the Azure Cosmos DB service. - /// This client is used to configure and execute requests against the service. - /// - /// - /// This type is thread safe. - /// - /// - /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. - /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. - /// - /// - /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. - /// Depending on your programming technique, you can do this in one of two ways: - /// - /// - /// - /// 1. By using a language construct such as the using statement in C#. - /// The using statement is actually a syntactic convenience. - /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. - /// - /// - /// - /// - /// - /// - /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. - /// The following example replaces the using block in the previous example with a try/catch/finally block. - /// - /// - /// - /// - /// - /// - internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal - { - private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; - private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; - private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; - private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; - private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; - private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; - private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; - private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; - private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; - private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; - private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; - private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; - private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; - private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; - private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; - // Env variable - private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; - - private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; - private const int DefaultMaxRequestsPerRntbdChannel = 30; - private const int DefaultRntbdPartitionCount = 1; - private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; - private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; - private const int DefaultRntbdPortPoolReuseThreshold = 256; - private const int DefaultRntbdPortPoolBindAttempts = 5; - private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; - private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; - private const bool DefaultEnableCpuMonitor = true; - private const string DefaultInitTaskKey = "InitTaskKey"; - - /// - /// Default thresholds for PPAF request hedging. +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Net.Security; + using System.Security; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using global::Azure.Core; + using Microsoft.Azure.Cosmos.Common; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Query; + using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Tracing.TraceData; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Client; + using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.FaultInjection; + using Microsoft.Azure.Documents.Routing; + using Newtonsoft.Json; + using ResourceType = Documents.ResourceType; + + /// + /// Provides a client-side logical representation for the Azure Cosmos DB service. + /// This client is used to configure and execute requests against the service. + /// + /// + /// This type is thread safe. + /// + /// + /// The service client that encapsulates the endpoint and credentials and connection policy used to access the Azure Cosmos DB service. + /// It is recommended to cache and reuse this instance within your application rather than creating a new instance for every operation. + /// + /// + /// When your app uses DocumentClient, you should call its IDisposable.Dispose implementation when you are finished using it. + /// Depending on your programming technique, you can do this in one of two ways: + /// + /// + /// + /// 1. By using a language construct such as the using statement in C#. + /// The using statement is actually a syntactic convenience. + /// At compile time, the language compiler implements the intermediate language (IL) for a try/catch block. + /// + /// + /// + /// + /// + /// + /// 2. By wrapping the call to the IDisposable.Dispose implementation in a try/catch block. + /// The following example replaces the using block in the previous example with a try/catch/finally block. + /// + /// + /// + /// + /// + /// + internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider, ICosmosAuthorizationTokenProvider, IDocumentClient, IDocumentClientInternal + { + private const string AllowOverrideStrongerConsistency = "AllowOverrideStrongerConsistency"; + private const string MaxConcurrentConnectionOpenConfig = "MaxConcurrentConnectionOpenRequests"; + private const string IdleConnectionTimeoutInSecondsConfig = "IdleConnectionTimeoutInSecondsConfig"; + private const string OpenConnectionTimeoutInSecondsConfig = "OpenConnectionTimeoutInSecondsConfig"; + private const string TransportTimerPoolGranularityInSecondsConfig = "TransportTimerPoolGranularityInSecondsConfig"; + private const string EnableTcpChannelConfig = "CosmosDbEnableTcpChannel"; + private const string MaxRequestsPerChannelConfig = "CosmosDbMaxRequestsPerTcpChannel"; + private const string TcpPartitionCount = "CosmosDbTcpPartitionCount"; + private const string MaxChannelsPerHostConfig = "CosmosDbMaxTcpChannelsPerHost"; + private const string RntbdPortReuseMode = "CosmosDbTcpPortReusePolicy"; + private const string RntbdPortPoolReuseThreshold = "CosmosDbTcpPortReuseThreshold"; + private const string RntbdPortPoolBindAttempts = "CosmosDbTcpPortReuseBindAttempts"; + private const string RntbdReceiveHangDetectionTimeConfig = "CosmosDbTcpReceiveHangDetectionTimeSeconds"; + private const string RntbdSendHangDetectionTimeConfig = "CosmosDbTcpSendHangDetectionTimeSeconds"; + private const string EnableCpuMonitorConfig = "CosmosDbEnableCpuMonitor"; + // Env variable + private const string RntbdMaxConcurrentOpeningConnectionCountConfig = "AZURE_COSMOS_TCP_MAX_CONCURRENT_OPENING_CONNECTION_COUNT"; + + private const int MaxConcurrentConnectionOpenRequestsPerProcessor = 25; + private const int DefaultMaxRequestsPerRntbdChannel = 30; + private const int DefaultRntbdPartitionCount = 1; + private const int DefaultMaxRntbdChannelsPerHost = ushort.MaxValue; + private const PortReuseMode DefaultRntbdPortReuseMode = PortReuseMode.ReuseUnicastPort; + private const int DefaultRntbdPortPoolReuseThreshold = 256; + private const int DefaultRntbdPortPoolBindAttempts = 5; + private const int DefaultRntbdReceiveHangDetectionTimeSeconds = 65; + private const int DefaultRntbdSendHangDetectionTimeSeconds = 10; + private const bool DefaultEnableCpuMonitor = true; + private const string DefaultInitTaskKey = "InitTaskKey"; + + /// + /// Default thresholds for PPAF request hedging. /// private const int DefaultHedgingThresholdInMilliseconds = 1000; private const int DefaultHedgingThresholdStepInMilliseconds = 500; - - private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; - private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; - - private readonly bool IsLocalQuorumConsistency = false; - private readonly bool isReplicaAddressValidationEnabled; - private readonly bool enableAsyncCacheExceptionNoSharing; - - //Fault Injection - private readonly IChaosInterceptorFactory chaosInterceptorFactory; - private readonly IChaosInterceptor chaosInterceptor; - - private bool isChaosInterceptorInititalized = false; - - //Auth + + private static readonly char[] resourceIdOrFullNameSeparators = new char[] { '/' }; + private static readonly char[] resourceIdSeparators = new char[] { '/', '\\', '?', '#' }; + + private readonly bool IsLocalQuorumConsistency = false; + private readonly bool isReplicaAddressValidationEnabled; + private readonly bool enableAsyncCacheExceptionNoSharing; + + //Fault Injection + private readonly IChaosInterceptorFactory chaosInterceptorFactory; + private readonly IChaosInterceptor chaosInterceptor; + + private bool isChaosInterceptorInititalized = false; + + //Auth internal readonly AuthorizationTokenProvider cosmosAuthorization; - private bool isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); - - // Gateway has backoff/retry logic to hide transient errors. - private RetryPolicy retryPolicy; - private bool allowOverrideStrongerConsistency = false; - private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; - private int openConnectionTimeoutInSeconds = 5; - private int idleConnectionTimeoutInSeconds = -1; - private int timerPoolGranularityInSeconds = 1; - private bool enableRntbdChannel = true; - private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; - private int rntbdPartitionCount = DefaultRntbdPartitionCount; - private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; - private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; - private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; - private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - private bool enableCpuMonitor = DefaultEnableCpuMonitor; - private int rntbdMaxConcurrentOpeningConnectionCount = 5; - private string clientId; - - //Consistency - private Documents.ConsistencyLevel? desiredConsistencyLevel; - - internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } - - internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } - - private ClientCollectionCache collectionCache; - - private PartitionKeyRangeCache partitionKeyRangeCache; - - //Private state. - private bool isSuccessfullyInitialized; - private bool isDisposed; - - // creator of TransportClient is responsible for disposing it. - private IStoreClientFactory storeClientFactory; - internal CosmosHttpClient httpClient { get; private set; } - - // Flag that indicates whether store client factory must be disposed whenever client is disposed. - // Setting this flag to false will result in store client factory not being disposed when client is disposed. - // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. - private bool isStoreClientFactoryCreatedInternally; - - //Id counter. - private static int idCounter; - //Trace Id. - private int traceId; - - //RemoteCertificateValidationCallback - internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; - - //Distributed Tracing Flag - internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; - - //SessionContainer. - internal ISessionContainer sessionContainer; - - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - - private AsyncLazy queryPartitionProvider; - - private DocumentClientEventSource eventSource; - private Func> initializeTaskFactory; - internal AsyncCacheNonBlocking initTaskCache; - - private JsonSerializerSettings serializerSettings; - private event EventHandler sendingRequest; - private event EventHandler receivedResponse; - private Func transportClientHandlerFactory; - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - { - if (authKey == null) - { - throw new ArgumentNullException("authKey"); - } - - if (authKey != null) - { - this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); - } - - this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The connection policy for the client. - /// - /// - /// This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings - /// for the Azure Cosmos DB service. - /// - /// - /// The service endpoint to use to create the client. - /// - /// - /// The list of Permission objects to use to create the client. - /// - /// - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// - /// (Optional) The connection policy for the client. If none is passed, the default is used - /// - /// - /// (Optional) This can be used to weaken the database account consistency level for read operations. - /// If this is not set the database account consistency level will be used for all requests. - /// - /// - /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. - /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed - /// - /// Using Direct connectivity, wherever possible, is recommended - /// - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - SecureString authKey, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - HttpMessageHandler handler, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) - { - } - - internal DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null) - : this(serviceEndpoint, - AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), - sendingRequestEventArgs, - connectionPolicy, - desiredConsistencyLevel, - serializerSettings, - apitype, - receivedResponseEventArgs, - handler, - sessionContainer, - enableCpuMonitor, - transportClientHandlerFactory, - storeClientFactory) - { - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token) and a connection policy - /// for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The cosmos authorization for the client. - /// The event handler to be invoked before the request is sent. - /// The event handler to be invoked after a response has been received. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// Api type for the account - /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). - /// The default session container with which DocumentClient is created. - /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. - /// Transport client handler factory. - /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. - /// Flag to allow Quorum Read with Eventual Consistency Account - /// - /// This delegate responsible for validating the third party certificate. - /// This is distributed tracing flag - /// This is the chaos interceptor used for fault injection - /// A boolean flag indicating if stack trace optimization is enabled. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - internal DocumentClient(Uri serviceEndpoint, - AuthorizationTokenProvider cosmosAuthorization, - EventHandler sendingRequestEventArgs, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - JsonSerializerSettings serializerSettings = null, - ApiType apitype = ApiType.None, - EventHandler receivedResponseEventArgs = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - Func transportClientHandlerFactory = null, - IStoreClientFactory storeClientFactory = null, - bool isLocalQuorumConsistency = false, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, - IChaosInterceptorFactory chaosInterceptorFactory = null, - bool enableAsyncCacheExceptionNoSharing = true) - { - if (sendingRequestEventArgs != null) - { - this.sendingRequest += sendingRequestEventArgs; - } - - if (serializerSettings != null) - { - this.serializerSettings = serializerSettings; - } - - this.ApiType = apitype; - - if (receivedResponseEventArgs != null) - { - this.receivedResponse += receivedResponseEventArgs; - } - - this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; - this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); - this.transportClientHandlerFactory = transportClientHandlerFactory; - this.IsLocalQuorumConsistency = isLocalQuorumConsistency; - this.initTaskCache = new AsyncCacheNonBlocking( - cancellationToken: this.cancellationTokenSource.Token, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.chaosInterceptorFactory = chaosInterceptorFactory; - this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); - - this.Initialize( - serviceEndpoint: serviceEndpoint, - connectionPolicy: connectionPolicy, - desiredConsistencyLevel: desiredConsistencyLevel, - handler: handler, - sessionContainer: sessionContainer, - enableCpuMonitor: enableCpuMonitor, - storeClientFactory: storeClientFactory, - cosmosClientId: cosmosClientId, - remoteCertificateValidationCallback: remoteCertificateValidationCallback, - cosmosClientTelemetryOptions: cosmosClientTelemetryOptions); - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The connection policy for the client. - /// The default consistency policy for client operations. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy, - Documents.ConsistencyLevel? desiredConsistencyLevel, - JsonSerializerSettings serializerSettings) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Initializes a new instance of the class using the - /// specified service endpoint, an authorization key (or resource token), a connection policy - /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. - /// - /// The service endpoint to use to create the client. - /// The authorization key or resource token to use to create the client. - /// The custom JsonSerializer settings to be used for serialization/derialization. - /// (Optional) The connection policy for the client. - /// (Optional) The default consistency policy for client operations. - /// - /// The service endpoint can be obtained from the Azure Management Portal. - /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal - /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. - /// - /// Using Direct connectivity, wherever possible, is recommended. - /// - /// - /// - /// - /// - /// - public DocumentClient(Uri serviceEndpoint, - string authKeyOrResourceToken, - JsonSerializerSettings serializerSettings, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null) - : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) - { - this.serializerSettings = serializerSettings; - } - - /// - /// Internal constructor purely for unit-testing - /// - internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) - { - // do nothing - this.ServiceEndpoint = serviceEndpoint; - this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); - } - - internal virtual async Task GetCollectionCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.collectionCache; - } - } - - internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) - { - using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) - { - await this.EnsureValidClientAsync(childTrace); - return this.partitionKeyRangeCache; - } - } - - internal GlobalAddressResolver AddressResolver { get; private set; } - - internal GlobalEndpointManager GlobalEndpointManager { get; private set; } - - internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } - - /// - /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. - /// - /// - /// A object. - /// - /// - /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. - /// If there are any initialization exceptions, this method will throw them (set on the task). - /// Alternately, calling any API will throw initialization exception at the first call. - /// - /// - /// - /// - /// - /// - public Task OpenAsync(CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); - } - - private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); - } - - private async Task OpenPrivateAsync(CancellationToken cancellationToken) - { - // Initialize caches for all databases and collections - ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( - new FeedOptions { MaxItemCount = -1 }); - - try - { - while (databaseFeedReader.HasMoreResults) - { - foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) - { - ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( - database.SelfLink, - new FeedOptions { MaxItemCount = -1 }); - List tasks = new List(); - while (collectionFeedReader.HasMoreResults) - { - tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); - } - - await Task.WhenAll(tasks); - } - } - } - catch (DocumentClientException ex) - { - // Clear the caches to ensure that we don't have partial results - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - - DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); - } - } - - internal virtual void Initialize(Uri serviceEndpoint, - ConnectionPolicy connectionPolicy = null, - Documents.ConsistencyLevel? desiredConsistencyLevel = null, - HttpMessageHandler handler = null, - ISessionContainer sessionContainer = null, - bool? enableCpuMonitor = null, - IStoreClientFactory storeClientFactory = null, - TokenCredential tokenCredential = null, - string cosmosClientId = null, - RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, - CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null) - { - if (serviceEndpoint == null) - { - throw new ArgumentNullException("serviceEndpoint"); - } - - this.clientId = cosmosClientId; - this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; - this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); - - this.queryPartitionProvider = new AsyncLazy(async () => - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); - }, CancellationToken.None); - -#if !(NETSTANDARD15 || NETSTANDARD16) -#if NETSTANDARD20 - // GetEntryAssembly returns null when loaded from native netstandard2.0 - if (System.Reflection.Assembly.GetEntryAssembly() != null) - { -#endif - // For tests we want to allow stronger consistency during construction or per call - string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; - if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) - { - if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) - { - this.allowOverrideStrongerConsistency = false; - } - } - - // We might want to override the defaults sometime - string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; - if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) - { - int maxConcurrentConnectionOpenRequestOverrideInt = 0; - if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) - { - this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; - } - } - - string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) - { - int openConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) - { - this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; - } - } - - string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; - if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) - { - int idleConnectionTimeoutInSecondsOverrideInt = 0; - if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) - { - this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; - } - } - - string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; - if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) - { - int timerPoolGranularityInSecondsOverrideInt = 0; - if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) - { - // timeoutgranularity specified should be greater than min(5 seconds) - if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) - { - this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; - } - } - } - - string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; - if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) - { - bool enableRntbdChannel = false; - if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) - { - this.enableRntbdChannel = enableRntbdChannel; - } - } - - string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; - if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) - { - int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; - if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) - { - this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; - } - } - - string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; - if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) - { - int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; - if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) - { - this.rntbdPartitionCount = rntbdPartitionCount; - } - } - - string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; - if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) - { - int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; - if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) - { - this.maxRntbdChannels = maxRntbdChannels; - } - } - - string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; - if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) - { - PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; - if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) - { - this.rntbdPortReuseMode = portReuseMode; - } - } - - string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; - if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) - { - int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; - if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) - { - this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; - } - } - - string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; - if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) - { - int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; - if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) - { - this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; - } - } - - string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) - { - int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; - if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) - { - this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; - } - } - - string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; - if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) - { - int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; - if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) - { - this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; - } - } - - if (enableCpuMonitor.HasValue) - { - this.enableCpuMonitor = enableCpuMonitor.Value; - } - else - { - string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; - if (!string.IsNullOrEmpty(enableCpuMonitorString)) - { - bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; - if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) - { - this.enableCpuMonitor = enableCpuMonitorFlag; - } - } - } -#if NETSTANDARD20 - } -#endif -#endif - - string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); - if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) - { - if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) - { - if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) - { - throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); - } - - this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; - } - } - - // ConnectionPolicy always overrides appconfig - if (connectionPolicy != null) - { - if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) - { - this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) - { - this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; - } - - if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) - { - this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; - } - - if (connectionPolicy.MaxTcpPartitionCount.HasValue) - { - this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; - } - - if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) - { - this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; - } - - if (connectionPolicy.PortReuseMode.HasValue) - { - this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; - } - } - - this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); - - this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; - -#if !NETSTANDARD16 - if (ServicePointAccessor.IsSupported) - { - ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); - servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; - } + private bool isThinClientEnabled = ConfigurationManager.IsThinClientEnabled(defaultValue: false); + + // Gateway has backoff/retry logic to hide transient errors. + private RetryPolicy retryPolicy; + private bool allowOverrideStrongerConsistency = false; + private int maxConcurrentConnectionOpenRequests = Environment.ProcessorCount * MaxConcurrentConnectionOpenRequestsPerProcessor; + private int openConnectionTimeoutInSeconds = 5; + private int idleConnectionTimeoutInSeconds = -1; + private int timerPoolGranularityInSeconds = 1; + private bool enableRntbdChannel = true; + private int maxRequestsPerRntbdChannel = DefaultMaxRequestsPerRntbdChannel; + private int rntbdPartitionCount = DefaultRntbdPartitionCount; + private int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + private PortReuseMode rntbdPortReuseMode = DefaultRntbdPortReuseMode; + private int rntbdPortPoolReuseThreshold = DefaultRntbdPortPoolReuseThreshold; + private int rntbdPortPoolBindAttempts = DefaultRntbdPortPoolBindAttempts; + private int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + private int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + private bool enableCpuMonitor = DefaultEnableCpuMonitor; + private int rntbdMaxConcurrentOpeningConnectionCount = 5; + private string clientId; + + //Consistency + private Documents.ConsistencyLevel? desiredConsistencyLevel; + + internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } + + internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } + + private ClientCollectionCache collectionCache; + + private PartitionKeyRangeCache partitionKeyRangeCache; + + //Private state. + private bool isSuccessfullyInitialized; + private bool isDisposed; + + // creator of TransportClient is responsible for disposing it. + private IStoreClientFactory storeClientFactory; + internal CosmosHttpClient httpClient { get; private set; } + + // Flag that indicates whether store client factory must be disposed whenever client is disposed. + // Setting this flag to false will result in store client factory not being disposed when client is disposed. + // This flag is used to allow shared store client factory survive disposition of a document client while other clients continue using it. + private bool isStoreClientFactoryCreatedInternally; + + //Id counter. + private static int idCounter; + //Trace Id. + private int traceId; + + //RemoteCertificateValidationCallback + internal RemoteCertificateValidationCallback remoteCertificateValidationCallback; + + //Distributed Tracing Flag + internal CosmosClientTelemetryOptions cosmosClientTelemetryOptions; + + //SessionContainer. + internal ISessionContainer sessionContainer; + + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + private AsyncLazy queryPartitionProvider; + + private DocumentClientEventSource eventSource; + private Func> initializeTaskFactory; + internal AsyncCacheNonBlocking initTaskCache; + + private JsonSerializerSettings serializerSettings; + private event EventHandler sendingRequest; + private event EventHandler receivedResponse; + private Func transportClientHandlerFactory; + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, and connection policy for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + { + if (authKey == null) + { + throw new ArgumentNullException("authKey"); + } + + if (authKey != null) + { + this.cosmosAuthorization = new AuthorizationTokenProviderMasterKey(authKey); + } + + this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel); + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy); + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The connection policy for the client. + /// + /// + /// This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified Azure Cosmos DB service endpoint, key, connection policy and a custom JsonSerializerSettings + /// for the Azure Cosmos DB service. + /// + /// + /// The service endpoint to use to create the client. + /// + /// + /// The list of Permission objects to use to create the client. + /// + /// + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// + /// (Optional) The connection policy for the client. If none is passed, the default is used + /// + /// + /// (Optional) This can be used to weaken the database account consistency level for read operations. + /// If this is not set the database account consistency level will be used for all requests. + /// + /// + /// The service endpoint and the authorization key can be obtained from the Azure Management Portal. + /// The authKey used here is encrypted for privacy when being used, and deleted from computer memory when no longer needed + /// + /// Using Direct connectivity, wherever possible, is recommended + /// + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + SecureString authKey, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKey, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + HttpMessageHandler handler, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel, handler: handler) + { + } + + internal DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null) + : this(serviceEndpoint, + AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(authKeyOrResourceToken), + sendingRequestEventArgs, + connectionPolicy, + desiredConsistencyLevel, + serializerSettings, + apitype, + receivedResponseEventArgs, + handler, + sessionContainer, + enableCpuMonitor, + transportClientHandlerFactory, + storeClientFactory) + { + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token) and a connection policy + /// for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The cosmos authorization for the client. + /// The event handler to be invoked before the request is sent. + /// The event handler to be invoked after a response has been received. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// Api type for the account + /// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler). + /// The default session container with which DocumentClient is created. + /// Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting. + /// Transport client handler factory. + /// Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process. + /// Flag to allow Quorum Read with Eventual Consistency Account + /// + /// This delegate responsible for validating the third party certificate. + /// This is distributed tracing flag + /// This is the chaos interceptor used for fault injection + /// A boolean flag indicating if stack trace optimization is enabled. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + internal DocumentClient(Uri serviceEndpoint, + AuthorizationTokenProvider cosmosAuthorization, + EventHandler sendingRequestEventArgs, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + JsonSerializerSettings serializerSettings = null, + ApiType apitype = ApiType.None, + EventHandler receivedResponseEventArgs = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + Func transportClientHandlerFactory = null, + IStoreClientFactory storeClientFactory = null, + bool isLocalQuorumConsistency = false, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null, + IChaosInterceptorFactory chaosInterceptorFactory = null, + bool enableAsyncCacheExceptionNoSharing = true) + { + if (sendingRequestEventArgs != null) + { + this.sendingRequest += sendingRequestEventArgs; + } + + if (serializerSettings != null) + { + this.serializerSettings = serializerSettings; + } + + this.ApiType = apitype; + + if (receivedResponseEventArgs != null) + { + this.receivedResponse += receivedResponseEventArgs; + } + + this.enableAsyncCacheExceptionNoSharing = enableAsyncCacheExceptionNoSharing; + this.cosmosAuthorization = cosmosAuthorization ?? throw new ArgumentNullException(nameof(cosmosAuthorization)); + this.transportClientHandlerFactory = transportClientHandlerFactory; + this.IsLocalQuorumConsistency = isLocalQuorumConsistency; + this.initTaskCache = new AsyncCacheNonBlocking( + cancellationToken: this.cancellationTokenSource.Token, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.chaosInterceptorFactory = chaosInterceptorFactory; + this.chaosInterceptor = chaosInterceptorFactory?.CreateInterceptor(this); + + this.Initialize( + serviceEndpoint: serviceEndpoint, + connectionPolicy: connectionPolicy, + desiredConsistencyLevel: desiredConsistencyLevel, + handler: handler, + sessionContainer: sessionContainer, + enableCpuMonitor: enableCpuMonitor, + storeClientFactory: storeClientFactory, + cosmosClientId: cosmosClientId, + remoteCertificateValidationCallback: remoteCertificateValidationCallback, + cosmosClientTelemetryOptions: cosmosClientTelemetryOptions); + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The connection policy for the client. + /// The default consistency policy for client operations. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + [Obsolete("Please use the constructor that takes JsonSerializerSettings as the third parameter.")] + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + ConnectionPolicy connectionPolicy, + Documents.ConsistencyLevel? desiredConsistencyLevel, + JsonSerializerSettings serializerSettings) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Initializes a new instance of the class using the + /// specified service endpoint, an authorization key (or resource token), a connection policy + /// and a custom JsonSerializerSettings for the Azure Cosmos DB service. + /// + /// The service endpoint to use to create the client. + /// The authorization key or resource token to use to create the client. + /// The custom JsonSerializer settings to be used for serialization/derialization. + /// (Optional) The connection policy for the client. + /// (Optional) The default consistency policy for client operations. + /// + /// The service endpoint can be obtained from the Azure Management Portal. + /// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal + /// If however you are connecting as a specific Azure Cosmos DB User, the value passed to is the ResourceToken obtained from the permission feed for the user. + /// + /// Using Direct connectivity, wherever possible, is recommended. + /// + /// + /// + /// + /// + /// + public DocumentClient(Uri serviceEndpoint, + string authKeyOrResourceToken, + JsonSerializerSettings serializerSettings, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null) + : this(serviceEndpoint, authKeyOrResourceToken, (HttpMessageHandler)null, connectionPolicy, desiredConsistencyLevel) + { + this.serializerSettings = serializerSettings; + } + + /// + /// Internal constructor purely for unit-testing + /// + internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) + { + // do nothing + this.ServiceEndpoint = serviceEndpoint; + this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); + } + + internal virtual async Task GetCollectionCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Collection Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.collectionCache; + } + } + + internal virtual async Task GetPartitionKeyRangeCacheAsync(ITrace trace) + { + using (ITrace childTrace = trace.StartChild("Get Partition Key Range Cache", TraceComponent.Routing, Tracing.TraceLevel.Info)) + { + await this.EnsureValidClientAsync(childTrace); + return this.partitionKeyRangeCache; + } + } + + internal GlobalAddressResolver AddressResolver { get; private set; } + + internal GlobalEndpointManager GlobalEndpointManager { get; private set; } + + internal GlobalPartitionEndpointManager PartitionKeyRangeLocation { get; private set; } + + /// + /// Open the connection to validate that the client initialization is successful in the Azure Cosmos DB service. + /// + /// + /// A object. + /// + /// + /// This method is recommended to be called, after the constructor, but before calling any other methods on the DocumentClient instance. + /// If there are any initialization exceptions, this method will throw them (set on the task). + /// Alternately, calling any API will throw initialization exception at the first call. + /// + /// + /// + /// + /// + /// + public Task OpenAsync(CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateInlineAsync(cancellationToken), null, cancellationToken); + } + + private async Task OpenPrivateInlineAsync(CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + await TaskHelper.InlineIfPossibleAsync(() => this.OpenPrivateAsync(cancellationToken), this.ResetSessionTokenRetryPolicy.GetRequestPolicy(), cancellationToken); + } + + private async Task OpenPrivateAsync(CancellationToken cancellationToken) + { + // Initialize caches for all databases and collections + ResourceFeedReader databaseFeedReader = this.CreateDatabaseFeedReader( + new FeedOptions { MaxItemCount = -1 }); + + try + { + while (databaseFeedReader.HasMoreResults) + { + foreach (Documents.Database database in await databaseFeedReader.ExecuteNextAsync(cancellationToken)) + { + ResourceFeedReader collectionFeedReader = this.CreateDocumentCollectionFeedReader( + database.SelfLink, + new FeedOptions { MaxItemCount = -1 }); + List tasks = new List(); + while (collectionFeedReader.HasMoreResults) + { + tasks.AddRange((await collectionFeedReader.ExecuteNextAsync(cancellationToken)).Select(collection => this.InitializeCachesAsync(database.Id, collection, cancellationToken))); + } + + await Task.WhenAll(tasks); + } + } + } + catch (DocumentClientException ex) + { + // Clear the caches to ensure that we don't have partial results + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + + DefaultTrace.TraceWarning("Exception occurred while OpenAsync. Exception Message: {0}", ex.Message); + } + } + + internal virtual void Initialize(Uri serviceEndpoint, + ConnectionPolicy connectionPolicy = null, + Documents.ConsistencyLevel? desiredConsistencyLevel = null, + HttpMessageHandler handler = null, + ISessionContainer sessionContainer = null, + bool? enableCpuMonitor = null, + IStoreClientFactory storeClientFactory = null, + TokenCredential tokenCredential = null, + string cosmosClientId = null, + RemoteCertificateValidationCallback remoteCertificateValidationCallback = null, + CosmosClientTelemetryOptions cosmosClientTelemetryOptions = null) + { + if (serviceEndpoint == null) + { + throw new ArgumentNullException("serviceEndpoint"); + } + + this.clientId = cosmosClientId; + this.remoteCertificateValidationCallback = remoteCertificateValidationCallback; + this.cosmosClientTelemetryOptions = cosmosClientTelemetryOptions ?? new CosmosClientTelemetryOptions(); + + this.queryPartitionProvider = new AsyncLazy(async () => + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return new QueryPartitionProvider(this.accountServiceConfiguration.QueryEngineConfiguration); + }, CancellationToken.None); + +#if !(NETSTANDARD15 || NETSTANDARD16) +#if NETSTANDARD20 + // GetEntryAssembly returns null when loaded from native netstandard2.0 + if (System.Reflection.Assembly.GetEntryAssembly() != null) + { +#endif + // For tests we want to allow stronger consistency during construction or per call + string allowOverrideStrongerConsistencyConfig = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.AllowOverrideStrongerConsistency]; + if (!string.IsNullOrEmpty(allowOverrideStrongerConsistencyConfig)) + { + if (!bool.TryParse(allowOverrideStrongerConsistencyConfig, out this.allowOverrideStrongerConsistency)) + { + this.allowOverrideStrongerConsistency = false; + } + } + + // We might want to override the defaults sometime + string maxConcurrentConnectionOpenRequestsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxConcurrentConnectionOpenConfig]; + if (!string.IsNullOrEmpty(maxConcurrentConnectionOpenRequestsOverrideString)) + { + int maxConcurrentConnectionOpenRequestOverrideInt = 0; + if (Int32.TryParse(maxConcurrentConnectionOpenRequestsOverrideString, out maxConcurrentConnectionOpenRequestOverrideInt)) + { + this.maxConcurrentConnectionOpenRequests = maxConcurrentConnectionOpenRequestOverrideInt; + } + } + + string openConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.OpenConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(openConnectionTimeoutInSecondsOverrideString)) + { + int openConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(openConnectionTimeoutInSecondsOverrideString, out openConnectionTimeoutInSecondsOverrideInt)) + { + this.openConnectionTimeoutInSeconds = openConnectionTimeoutInSecondsOverrideInt; + } + } + + string idleConnectionTimeoutInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.IdleConnectionTimeoutInSecondsConfig]; + if (!string.IsNullOrEmpty(idleConnectionTimeoutInSecondsOverrideString)) + { + int idleConnectionTimeoutInSecondsOverrideInt = 0; + if (Int32.TryParse(idleConnectionTimeoutInSecondsOverrideString, out idleConnectionTimeoutInSecondsOverrideInt)) + { + this.idleConnectionTimeoutInSeconds = idleConnectionTimeoutInSecondsOverrideInt; + } + } + + string transportTimerPoolGranularityInSecondsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TransportTimerPoolGranularityInSecondsConfig]; + if (!string.IsNullOrEmpty(transportTimerPoolGranularityInSecondsOverrideString)) + { + int timerPoolGranularityInSecondsOverrideInt = 0; + if (Int32.TryParse(transportTimerPoolGranularityInSecondsOverrideString, out timerPoolGranularityInSecondsOverrideInt)) + { + // timeoutgranularity specified should be greater than min(5 seconds) + if (timerPoolGranularityInSecondsOverrideInt > this.timerPoolGranularityInSeconds) + { + this.timerPoolGranularityInSeconds = timerPoolGranularityInSecondsOverrideInt; + } + } + } + + string enableRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableTcpChannelConfig]; + if (!string.IsNullOrEmpty(enableRntbdChannelOverrideString)) + { + bool enableRntbdChannel = false; + if (bool.TryParse(enableRntbdChannelOverrideString, out enableRntbdChannel)) + { + this.enableRntbdChannel = enableRntbdChannel; + } + } + + string maxRequestsPerRntbdChannelOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxRequestsPerChannelConfig]; + if (!string.IsNullOrEmpty(maxRequestsPerRntbdChannelOverrideString)) + { + int maxRequestsPerChannel = DocumentClient.DefaultMaxRequestsPerRntbdChannel; + if (int.TryParse(maxRequestsPerRntbdChannelOverrideString, out maxRequestsPerChannel)) + { + this.maxRequestsPerRntbdChannel = maxRequestsPerChannel; + } + } + + string rntbdPartitionCountOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.TcpPartitionCount]; + if (!string.IsNullOrEmpty(rntbdPartitionCountOverrideString)) + { + int rntbdPartitionCount = DocumentClient.DefaultRntbdPartitionCount; + if (int.TryParse(rntbdPartitionCountOverrideString, out rntbdPartitionCount)) + { + this.rntbdPartitionCount = rntbdPartitionCount; + } + } + + string maxRntbdChannelsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.MaxChannelsPerHostConfig]; + if (!string.IsNullOrEmpty(maxRntbdChannelsOverrideString)) + { + int maxRntbdChannels = DefaultMaxRntbdChannelsPerHost; + if (int.TryParse(maxRntbdChannelsOverrideString, out maxRntbdChannels)) + { + this.maxRntbdChannels = maxRntbdChannels; + } + } + + string rntbdPortReuseModeOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortReuseMode]; + if (!string.IsNullOrEmpty(rntbdPortReuseModeOverrideString)) + { + PortReuseMode portReuseMode = DefaultRntbdPortReuseMode; + if (Enum.TryParse(rntbdPortReuseModeOverrideString, out portReuseMode)) + { + this.rntbdPortReuseMode = portReuseMode; + } + } + + string rntbdPortPoolReuseThresholdOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolReuseThreshold]; + if (!string.IsNullOrEmpty(rntbdPortPoolReuseThresholdOverrideString)) + { + int rntbdPortPoolReuseThreshold = DocumentClient.DefaultRntbdPortPoolReuseThreshold; + if (int.TryParse(rntbdPortPoolReuseThresholdOverrideString, out rntbdPortPoolReuseThreshold)) + { + this.rntbdPortPoolReuseThreshold = rntbdPortPoolReuseThreshold; + } + } + + string rntbdPortPoolBindAttemptsOverrideString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdPortPoolBindAttempts]; + if (!string.IsNullOrEmpty(rntbdPortPoolBindAttemptsOverrideString)) + { + int rntbdPortPoolBindAttempts = DocumentClient.DefaultRntbdPortPoolBindAttempts; + if (int.TryParse(rntbdPortPoolBindAttemptsOverrideString, out rntbdPortPoolBindAttempts)) + { + this.rntbdPortPoolBindAttempts = rntbdPortPoolBindAttempts; + } + } + + string rntbdReceiveHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdReceiveHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdReceiveHangDetectionTimeSecondsString)) + { + int rntbdReceiveHangDetectionTimeSeconds = DefaultRntbdReceiveHangDetectionTimeSeconds; + if (int.TryParse(rntbdReceiveHangDetectionTimeSecondsString, out rntbdReceiveHangDetectionTimeSeconds)) + { + this.rntbdReceiveHangDetectionTimeSeconds = rntbdReceiveHangDetectionTimeSeconds; + } + } + + string rntbdSendHangDetectionTimeSecondsString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.RntbdSendHangDetectionTimeConfig]; + if (!string.IsNullOrEmpty(rntbdSendHangDetectionTimeSecondsString)) + { + int rntbdSendHangDetectionTimeSeconds = DefaultRntbdSendHangDetectionTimeSeconds; + if (int.TryParse(rntbdSendHangDetectionTimeSecondsString, out rntbdSendHangDetectionTimeSeconds)) + { + this.rntbdSendHangDetectionTimeSeconds = rntbdSendHangDetectionTimeSeconds; + } + } + + if (enableCpuMonitor.HasValue) + { + this.enableCpuMonitor = enableCpuMonitor.Value; + } + else + { + string enableCpuMonitorString = System.Configuration.ConfigurationManager.AppSettings[DocumentClient.EnableCpuMonitorConfig]; + if (!string.IsNullOrEmpty(enableCpuMonitorString)) + { + bool enableCpuMonitorFlag = DefaultEnableCpuMonitor; + if (bool.TryParse(enableCpuMonitorString, out enableCpuMonitorFlag)) + { + this.enableCpuMonitor = enableCpuMonitorFlag; + } + } + } +#if NETSTANDARD20 + } +#endif +#endif + + string rntbdMaxConcurrentOpeningConnectionCountOverrideString = Environment.GetEnvironmentVariable(RntbdMaxConcurrentOpeningConnectionCountConfig); + if (!string.IsNullOrEmpty(rntbdMaxConcurrentOpeningConnectionCountOverrideString)) + { + if (Int32.TryParse(rntbdMaxConcurrentOpeningConnectionCountOverrideString, out int rntbdMaxConcurrentOpeningConnectionCountOverrideInt)) + { + if (rntbdMaxConcurrentOpeningConnectionCountOverrideInt <= 0) + { + throw new ArgumentException("RntbdMaxConcurrentOpeningConnectionCountConfig should be larger than 0"); + } + + this.rntbdMaxConcurrentOpeningConnectionCount = rntbdMaxConcurrentOpeningConnectionCountOverrideInt; + } + } + + // ConnectionPolicy always overrides appconfig + if (connectionPolicy != null) + { + if (connectionPolicy.IdleTcpConnectionTimeout.HasValue) + { + this.idleConnectionTimeoutInSeconds = (int)connectionPolicy.IdleTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.OpenTcpConnectionTimeout.HasValue) + { + this.openConnectionTimeoutInSeconds = (int)connectionPolicy.OpenTcpConnectionTimeout.Value.TotalSeconds; + } + + if (connectionPolicy.MaxRequestsPerTcpConnection.HasValue) + { + this.maxRequestsPerRntbdChannel = connectionPolicy.MaxRequestsPerTcpConnection.Value; + } + + if (connectionPolicy.MaxTcpPartitionCount.HasValue) + { + this.rntbdPartitionCount = connectionPolicy.MaxTcpPartitionCount.Value; + } + + if (connectionPolicy.MaxTcpConnectionsPerEndpoint.HasValue) + { + this.maxRntbdChannels = connectionPolicy.MaxTcpConnectionsPerEndpoint.Value; + } + + if (connectionPolicy.PortReuseMode.HasValue) + { + this.rntbdPortReuseMode = connectionPolicy.PortReuseMode.Value; + } + } + + this.ServiceEndpoint = serviceEndpoint.OriginalString.EndsWith("/", StringComparison.Ordinal) ? serviceEndpoint : new Uri(serviceEndpoint.OriginalString + "/"); + + this.ConnectionPolicy = connectionPolicy ?? ConnectionPolicy.Default; + +#if !NETSTANDARD16 + if (ServicePointAccessor.IsSupported) + { + ServicePointAccessor servicePoint = ServicePointAccessor.FindServicePoint(this.ServiceEndpoint); + servicePoint.ConnectionLimit = this.ConnectionPolicy.MaxConnectionLimit; + } #endif this.GlobalEndpointManager = new GlobalEndpointManager(this, this.ConnectionPolicy, this.enableAsyncCacheExceptionNoSharing); - - this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( - this.ApiType, - DocumentClientEventSource.Instance, - this.ConnectionPolicy, - handler, - this.sendingRequest, - this.receivedResponse, - this.chaosInterceptor); - - // Loading VM Information (non blocking call and initialization won't fail if this call fails) - VmMetadataApiHandler.TryInitialize(this.httpClient); - - if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) - { - CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); - CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); - - CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); - } - - // Starting ClientTelemetry Job - this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, - this.ConnectionPolicy, - this.cosmosAuthorization, - this.httpClient, - this.ServiceEndpoint, - this.GlobalEndpointManager, - this.cancellationTokenSource, - this.chaosInterceptor is not null); - - if (sessionContainer != null) - { - this.sessionContainer = sessionContainer; - } - else - { - this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); - } - - this.desiredConsistencyLevel = desiredConsistencyLevel; - // Setup the proxy to be used based on connection mode. - // For gateway: GatewayProxy. - // For direct: WFStoreProxy [set in OpenAsync()]. - this.eventSource = DocumentClientEventSource.Instance; - - this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( - () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), - new ResourceThrottleRetryPolicy( - this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, - this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); - - // Create the task to start the initialize task - // Task will be awaited on in the EnsureValidClientAsync - Task initTask = this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - - // ContinueWith on the initialization task is needed for handling the UnobservedTaskException - // if this task throws for some reason. Awaiting inside a constructor is not supported and - // even if we had to await inside GetInitializationTask to catch the exception, that will - // be a blocking call. In such cases, the recommended approach is to "handle" the - // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted - // and accessing the Exception property on the target task. -#pragma warning disable VSTHRD110 // Observe result of async calls -#pragma warning disable CDX1000 // DontConvertExceptionToObject - initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); -#pragma warning restore CDX1000 // DontConvertExceptionToObject -#pragma warning restore VSTHRD110 // Observe result of async calls - - this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); - DefaultTrace.TraceInformation(string.Format( - CultureInfo.InvariantCulture, - "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", - this.traceId, - serviceEndpoint.ToString(), - this.ConnectionPolicy.ConnectionMode.ToString(), - this.ConnectionPolicy.ConnectionProtocol.ToString(), - desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); - - this.QueryCompatibilityMode = QueryCompatibilityMode.Default; - } - - // Always called from under the lock except when called from Intilialize method during construction. - private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) - { - await this.InitializeGatewayConfigurationReaderAsync(); - - if (this.desiredConsistencyLevel.HasValue) - { - this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); + + this.httpClient = CosmosHttpClientCore.CreateWithConnectionPolicy( + this.ApiType, + DocumentClientEventSource.Instance, + this.ConnectionPolicy, + handler, + this.sendingRequest, + this.receivedResponse, + this.chaosInterceptor); + + // Loading VM Information (non blocking call and initialization won't fail if this call fails) + VmMetadataApiHandler.TryInitialize(this.httpClient); + + if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) + { + CosmosDbOperationMeter.Initialize(this.cosmosClientTelemetryOptions); + CosmosDbNetworkMeter.Initialize(this.cosmosClientTelemetryOptions); + + CosmosDbOperationMeter.AddInstanceCount(this.ServiceEndpoint); + } + + // Starting ClientTelemetry Job + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, + this.ConnectionPolicy, + this.cosmosAuthorization, + this.httpClient, + this.ServiceEndpoint, + this.GlobalEndpointManager, + this.cancellationTokenSource, + this.chaosInterceptor is not null); + + if (sessionContainer != null) + { + this.sessionContainer = sessionContainer; + } + else + { + this.sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); + } + + this.desiredConsistencyLevel = desiredConsistencyLevel; + // Setup the proxy to be used based on connection mode. + // For gateway: GatewayProxy. + // For direct: WFStoreProxy [set in OpenAsync()]. + this.eventSource = DocumentClientEventSource.Instance; + + this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( + () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), + new ResourceThrottleRetryPolicy( + this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests, + this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds)); + + // Create the task to start the initialize task + // Task will be awaited on in the EnsureValidClientAsync + Task initTask = this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + + // ContinueWith on the initialization task is needed for handling the UnobservedTaskException + // if this task throws for some reason. Awaiting inside a constructor is not supported and + // even if we had to await inside GetInitializationTask to catch the exception, that will + // be a blocking call. In such cases, the recommended approach is to "handle" the + // UnobservedTaskException by using ContinueWith method w/ TaskContinuationOptions.OnlyOnFaulted + // and accessing the Exception property on the target task. +#pragma warning disable VSTHRD110 // Observe result of async calls +#pragma warning disable CDX1000 // DontConvertExceptionToObject + initTask.ContinueWith(t => DefaultTrace.TraceWarning("initializeTask failed {0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted); +#pragma warning restore CDX1000 // DontConvertExceptionToObject +#pragma warning restore VSTHRD110 // Observe result of async calls + + this.traceId = Interlocked.Increment(ref DocumentClient.idCounter); + DefaultTrace.TraceInformation(string.Format( + CultureInfo.InvariantCulture, + "DocumentClient with id {0} initialized at endpoint: {1} with ConnectionMode: {2}, connection Protocol: {3}, and consistency level: {4}", + this.traceId, + serviceEndpoint.ToString(), + this.ConnectionPolicy.ConnectionMode.ToString(), + this.ConnectionPolicy.ConnectionProtocol.ToString(), + desiredConsistencyLevel != null ? desiredConsistencyLevel.ToString() : "null")); + + this.QueryCompatibilityMode = QueryCompatibilityMode.Default; + } + + // Always called from under the lock except when called from Intilialize method during construction. + private async Task GetInitializationTaskAsync(IStoreClientFactory storeClientFactory) + { + await this.InitializeGatewayConfigurationReaderAsync(); + + if (this.desiredConsistencyLevel.HasValue) + { + this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value); } if (!this.ConnectionPolicy.DisablePartitionLevelFailoverClientLevelOverride @@ -1074,37 +1074,36 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli this.isThinClientEnabled) : GlobalPartitionEndpointManagerNoOp.Instance; - this.retryPolicy = new RetryPolicy( - globalEndpointManager: this.GlobalEndpointManager, - connectionPolicy: this.ConnectionPolicy, + this.retryPolicy = new RetryPolicy( + globalEndpointManager: this.GlobalEndpointManager, + connectionPolicy: this.ConnectionPolicy, partitionKeyRangeLocationCache: this.PartitionKeyRangeLocation, - isThinClientEnabled: this.isThinClientEnabled); - - this.ResetSessionTokenRetryPolicy = this.retryPolicy; - - GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( - this.GlobalEndpointManager, - this.sessionContainer, - (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, - this.eventSource, - this.serializerSettings, - this.httpClient, - this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, - this.chaosInterceptor); - - this.GatewayStoreModel = gatewayStoreModel; - - this.collectionCache = new ClientCollectionCache( - sessionContainer: this.sessionContainer, - storeModel: this.GatewayStoreModel, - tokenProvider: this, - retryPolicy: this.retryPolicy, - telemetryToServiceHelper: this.telemetryToServiceHelper, - enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); - this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); - this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); - + isThinClientEnabled: this.isThinClientEnabled); + + this.ResetSessionTokenRetryPolicy = this.retryPolicy; + + GatewayStoreModel gatewayStoreModel = new GatewayStoreModel( + this.GlobalEndpointManager, + this.sessionContainer, + (Cosmos.ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel, + this.eventSource, + this.serializerSettings, + this.httpClient, + this.PartitionKeyRangeLocation, + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + + this.GatewayStoreModel = gatewayStoreModel; + + this.collectionCache = new ClientCollectionCache( + sessionContainer: this.sessionContainer, + storeModel: this.GatewayStoreModel, + tokenProvider: this, + retryPolicy: this.retryPolicy, + telemetryToServiceHelper: this.telemetryToServiceHelper, + enableAsyncCacheExceptionNoSharing: this.enableAsyncCacheExceptionNoSharing); + this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache, this.GlobalEndpointManager, this.enableAsyncCacheExceptionNoSharing); + this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); + gatewayStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); if (this.ConnectionPolicy.ConnectionMode == ConnectionMode.Gateway) @@ -1121,7 +1120,7 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli this.httpClient, this.ConnectionPolicy.UserAgentContainer, isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, - chaosInterceptor: this.chaosInterceptor); + this.chaosInterceptor); thinClientStoreModel.SetCaches(this.partitionKeyRangeCache, this.collectionCache); @@ -1131,5747 +1130,5747 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli { this.StoreModel = this.GatewayStoreModel; } - } - else - { - this.InitializeDirectConnectivity(storeClientFactory); - } - - return true; - } + } + else + { + this.InitializeDirectConnectivity(storeClientFactory); + } + + return true; + } + + private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) + { + if (databaseName == null) + { + throw new ArgumentNullException(nameof(databaseName)); + } + + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + using ( + DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Query, + ResourceType.Document, + collection.SelfLink, + AuthorizationTokenType.PrimaryMasterKey)) + { + ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( + resolvedCollection.ResourceId, + new Range( + PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, + PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, + true, + false), + NoOpTrace.Singleton); + + // In Gateway mode, AddressCache is null + if (this.AddressResolver != null) + { + await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); + } + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. + /// + /// + /// + /// The session object used for version tracking when the consistency level is set to Session. + /// + /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. + /// + public object Session + { + get + { + return this.sessionContainer; + } + + set + { + SessionContainer container = value as SessionContainer; + if (container == null) + { + throw new ArgumentNullException("value"); + } + + if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + ClientResources.BadSession, + container.HostName, + this.ServiceEndpoint.Host)); + } + + SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; + if (currentSessionContainer == null) + { + throw new ArgumentNullException(nameof(currentSessionContainer)); + } + + currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); + } + } + + /// + /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. + /// + /// Collection for which session token must be retrieved. + /// + /// The session token used for version tracking when the consistency level is set to Session. + /// + /// + /// The session token can be saved and supplied to a request via . + /// + internal string GetSessionToken(string collectionLink) + { + SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; + + if (sessionContainerInternal == null) + { + throw new ArgumentNullException(nameof(sessionContainerInternal)); + } + + return sessionContainerInternal.GetSessionToken(collectionLink); + } + + /// + /// Gets the Api type + /// + internal ApiType ApiType + { + get; private set; + } + + internal bool UseMultipleWriteLocations { get; private set; } + + /// + /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. + /// + /// + /// The Uri for the service endpoint. + /// + /// + public Uri ServiceEndpoint + { + get; + private set; + } + + /// + /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri WriteEndpoint + { + get + { + return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. + /// + public Uri ReadEndpoint + { + get + { + return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); + } + } + + /// + /// Gets the Connection policy used by the client from the Azure Cosmos DB service. + /// + /// + /// The Connection policy used by the client. + /// + /// + public ConnectionPolicy ConnectionPolicy { get; private set; } + + /// + /// Gets the AuthKey used by the client from the Azure Cosmos DB service. + /// + /// + /// The AuthKey used by the client. + /// + /// + public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); + + /// + /// Gets the configured consistency level of the client from the Azure Cosmos DB service. + /// + /// + /// The configured of the client. + /// + /// + public virtual Documents.ConsistencyLevel ConsistencyLevel + { + get + { +#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits + TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); +#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits + return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : + this.accountServiceConfiguration.DefaultConsistencyLevel; + } + } + + /// + /// Returns the account properties available in the service configuration if the client was initialized. + /// + public bool TryGetCachedAccountProperties(out AccountProperties properties) + { + if (this.isSuccessfullyInitialized + && this.accountServiceConfiguration != null + && this.accountServiceConfiguration.AccountProperties != null) + { + properties = this.accountServiceConfiguration.AccountProperties; + return true; + } + + properties = null; + return false; + } + + /// + /// Disposes the client for the Azure Cosmos DB service. + /// + /// + /// + /// + /// + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + if (this.telemetryToServiceHelper != null) + { + this.telemetryToServiceHelper.Dispose(); + this.telemetryToServiceHelper = null; + } + + if (!this.cancellationTokenSource.IsCancellationRequested) + { + this.cancellationTokenSource.Cancel(); + } + + this.cancellationTokenSource.Dispose(); + + if (this.StoreModel != null) + { + this.StoreModel.Dispose(); + this.StoreModel = null; + } + + if (this.storeClientFactory != null) + { + // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference + if (this.isStoreClientFactoryCreatedInternally) + { + this.storeClientFactory.Dispose(); + } + + this.storeClientFactory = null; + } + + if (this.AddressResolver != null) + { + this.AddressResolver.Dispose(); + this.AddressResolver = null; + } + + if (this.httpClient != null) + { + try + { + this.httpClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.httpClient = null; + } + + if (this.cosmosAuthorization != null) + { + this.cosmosAuthorization.Dispose(); + } + + if (this.GlobalEndpointManager != null) + { + this.GlobalEndpointManager.Dispose(); + this.GlobalEndpointManager = null; + } + + if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) + { + this.queryPartitionProvider.Value.Dispose(); + } + + if (this.initTaskCache != null) + { + this.initTaskCache.Dispose(); + this.initTaskCache = null; + } + + DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); + DefaultTrace.Flush(); + + this.isDisposed = true; + } + + //Compatibility mode: + // Allows to specify compatibility mode used by client when making query requests. + // should be removed when application/sql is no longer supported. + internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } + + /// + /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). + /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache + /// to resolves the request's collection name. If it differs from the session container's resource id it + /// explains the session unavailable exception: somebody removed and recreated the collection. In this + /// case we retry once again (with empty session token) otherwise we return the error to the client + /// (see RenameCollectionAwareClientRetryPolicy) + /// + internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } + + /// + /// Gets and sets the IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension StoreModel { get; set; } + + /// + /// Gets and sets the gateway IStoreModel object. + /// + /// + /// Test hook to enable unit test of DocumentClient. + /// + internal IStoreModelExtension GatewayStoreModel { get; set; } + + /// + /// Gets and sets on execute scalar query callback + /// + /// + /// Test hook to enable unit test for scalar queries + /// + internal Action OnExecuteScalarQueryCallback { get; set; } + + internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; + + internal virtual async Task GetDefaultConsistencyLevelAsync() + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; + } + + internal Task GetDesiredConsistencyLevelAsync() + { + return Task.FromResult(this.desiredConsistencyLevel); + } + + internal async Task ProcessRequestAsync( + string verb, + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + string testAuthorization = null) // Only for unit-tests + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (verb == null) + { + throw new ArgumentNullException(nameof(verb)); + } + + (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( + request.ResourceAddress, + PathsHelper.GetResourcePath(request.ResourceType), + verb, + request.Headers, + AuthorizationTokenType.PrimaryMasterKey); + + // Unit-test hook + if (testAuthorization != null) + { + payload = testAuthorization; + authorization = testAuthorization; + } + request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; + + try + { + return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); + } + catch (DocumentClientException dce) + { + this.cosmosAuthorization.TraceUnauthorized( + dce, + authorization, + payload); + + throw; + } + } + + internal Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); + } + + internal async Task ProcessRequestAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicyInstance, + ITrace trace, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(trace); + + retryPolicyInstance?.OnBeforeSendRequest(request); + + using (new ActivityScope(Guid.NewGuid())) + { + IStoreModel storeProxy = this.GetStoreProxy(request); + return await storeProxy.ProcessMessageAsync(request, cancellationToken); + } + } + + /// + /// Establishes and Initializes the Rntbd connection to all the backend replica nodes + /// for the given database name and container. + /// + /// A string containing the cosmos database name. + /// A string containing the cosmos container link uri. + /// An instance of the . + internal async Task OpenConnectionsToAllReplicasAsync( + string databaseName, + string containerLinkUri, + CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(databaseName) || + string.IsNullOrEmpty(containerLinkUri)) + { + string resourceName = string.IsNullOrEmpty(databaseName) ? + nameof(databaseName) : + nameof(containerLinkUri); + + throw new ArgumentNullException(resourceName); + } + + if (this.StoreModel != null) + { + try + { + await this.StoreModel.OpenConnectionsToAllReplicasAsync( + databaseName, + containerLinkUri, + cancellationToken); + } + catch (Exception) + { + throw; + } + } + } + + private static string NormalizeAuthorizationPayload(string input) + { + const int expansionBuffer = 12; + StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); + for (int i = 0; i < input.Length; i++) + { + switch (input[i]) + { + case '\n': + builder.Append("\\n"); + break; + case '/': + builder.Append("\\/"); + break; + default: + builder.Append(input[i]); + break; + } + } + + return builder.ToString(); + } + + internal async Task InitilizeFaultInjectionAsync() + { + if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) + { + this.isChaosInterceptorInititalized = true; + await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); + } + } + + internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) + { + return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.maxRequestsPerRntbdChannel, + this.maxRntbdChannels, + this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + this.rntbdPortReuseMode); + } + + internal virtual async Task EnsureValidClientAsync(ITrace trace) + { + if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) + { + return; + } + + // Trace when the Initialization of client has not been completed. Usually during first call + using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) + { + // If the initialization task failed, we should retry initialization. + // We may end up throwing the same exception but this will ensure that we dont have a + // client which is unusable and can resume working if it failed initialization once. + // If we have to reinitialize the client, it needs to happen in thread safe manner so that + // we dont re-initalize the task again for each incoming call. + try + { + this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( + key: DocumentClient.DefaultInitTaskKey, + singleValueInitFunc: this.initializeTaskFactory, + forceRefresh: (_) => false); + } + catch (DocumentClientException ex) + { + throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( + dce: ex, + trace: trace); + } + catch (Exception e) + { + DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); + childTrace.AddDatum("initializeTask failed", e.Message); + throw; + } + + await this.InitilizeFaultInjectionAsync(); + } + } + + #region Create Impl + /// + /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed. + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. + /// + /// The example below creates a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); + } + + private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, + Documents.Client.RequestOptions options) + { + if (database == null) + { + throw new ArgumentNullException("database"); + } + + // Doing a Read before Create will give us better latency for existing databases + try + { + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDatabaseAsync(database, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); + } + + /// + /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to create. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// Finally, a Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Create a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, + object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, + CancellationToken cancellationToken = default) + { + // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy); + } + + private async Task> CreateDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 collection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateDocumentCollectionPrivateAsync( + string databaseLink, + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse( + await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. + /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). + /// + /// The link of the database to create the collection in. E.g. dbs/db_rid/. + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 DocumentCollection are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); + } + + private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( + string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) + { + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make + // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. + Documents.Database database = await this.ReadDatabaseAsync(databaseLink); + + // Doing a Read before Create will give us better latency for existing collections. + // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, + // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. + try + { + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + catch (DocumentClientException dce) + { + if (dce.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + try + { + return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); + } + catch (DocumentClientException ex) + { + if (ex.StatusCode != HttpStatusCode.Conflict) + { + throw; + } + } + + // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create + // so for the remaining ones we should do a Read instead of throwing Conflict exception + return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); + } + + /// + /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link to the source object. + /// The target object. + /// (optional)The point in time to restore. If null, use the latest restorable time. + /// (Optional) The for the request. + /// The task object representing the service response for the asynchronous operation. + internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + if (targetDocumentCollection == null) + { + throw new ArgumentNullException("targetDocumentCollection"); + } + + bool isFeed; + string resourceTypeString; + string resourceIdOrFullName; + bool isNameBased; + + string dbsId; + string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); + if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + dbsId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + string sourceCollId; + if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) + { + string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); + sourceCollId = segments[segments.Length - 1]; + } + else + { + throw new ArgumentNullException("sourceDocumentCollectionLink"); + } + + this.ValidateResource(targetDocumentCollection); + + if (options == null) + { + options = new Documents.Client.RequestOptions(); + } + if (!options.RemoteStorageType.HasValue) + { + options.RemoteStorageType = RemoteStorageType.Standard; + } + options.SourceDatabaseId = dbsId; + options.SourceCollectionId = sourceCollId; + if (restoreTime.HasValue) + { + options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + targetDocumentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + // set the session token + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + return collection; + } + } + + /// + /// Get the status of a collection being restored in the Azure Cosmos DB service. + /// + /// The link of the document collection being restored. + /// The task object representing the service response for the asynchronous operation. + internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (string.IsNullOrEmpty(targetDocumentCollectionLink)) + { + throw new ArgumentNullException("targetDocumentCollectionLink"); + } + + ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( + targetDocumentCollectionLink, + new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, + retryPolicyInstance); + string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); + if (restoreState == null) + { + restoreState = RestoreState.RestoreCompleted.ToString(); + } + + DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() + { + State = restoreState + }; + + return ret; + } + + /// + /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// The that was created contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to create. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ + /// The object to create. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the created object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to create. + /// (Optional) The for the request. + /// The that was created within a task object representing the service response for the asynchronous operation. + /// If is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. + /// + /// + /// 409 + /// + /// Conflict - This means a with an id matching the id field of already existed, + /// or there is already a pending snapshot for the specified resource link. + /// + /// + /// + /// + /// + /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that + /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". + /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// If you would like to construct a from within a synchronous method then you need to use the following code + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (snapshot == null) + { + throw new ArgumentNullException("snapshot"); + } + + this.ValidateResource(snapshot); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Create, + Paths.Snapshots_Root, + snapshot, + ResourceType.Snapshot, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Delete Impl + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + /// + /// Delete a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the to delete. E.g. snapshots/snapshot_rid/ + /// (Optional) The request options for the request. + /// A containing a which will contain information about the request issued. + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Delete, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Replace Impl + /// + /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. + /// + /// the updated document collection. + /// the request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceDocumentCollectionPrivateAsync( + DocumentCollection documentCollection, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (documentCollection == null) + { + throw new ArgumentNullException("documentCollection"); + } + + this.ValidateResource(documentCollection); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(documentCollection), + documentCollection, + ResourceType.Collection, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + // set the session token + if (collection.Resource != null) + { + this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); + } + return collection; + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If either or is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now dynamically cast doc back to your MyPoco + /// MyPoco poco = (dynamic)doc; + /// + /// //Update some properties of the poco object + /// poco.MyProperty = "updated value"; + /// + /// //Now persist these changes to the database using doc.SelLink and the update poco object + /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if ((options == null) || (options.PartitionKey == null)) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible( + () => this.ReplaceDocumentPrivateAsync( + documentLink, + document, + options, + requestRetryPolicy, + cancellationToken), + requestRetryPolicy, + cancellationToken); + } + + private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document + /// + /// (collectionLink) + /// .Where(r => r.Id == "doc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// doc.SetPropertyValue("MyProperty", "updated value"); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Document updated = await client.ReplaceDocumentAsync(doc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( + this.GetLinkForRouting(document), + document, + options, + retryPolicyInstance, + cancellationToken), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + this.ValidateResource(document); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + documentLink, + document, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, document, options); + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "sproc id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// sproc.Body = "function () {new javascript body for sproc}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceStoredProcedurePrivateAsync( + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(storedProcedure), + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "trigger id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// trigger.Body = "function () {new javascript body for trigger}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// Trigger updated = await client.ReplaceTriggerAsync(sproc); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(trigger), + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "udf id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Update some properties on the found resource + /// udf.Body = "function () {new javascript body for udf}"; + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedFunctionPrivateAsync( + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(function), + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. + /// + /// + /// + /// + /// + /// r.ResourceLink == "collection selfLink") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Create a new offer with the changed throughput + /// OfferV2 newOffer = new OfferV2(offer, 5000); + /// + /// //Now persist these changes to the database by replacing the original resource + /// Offer updated = await client.ReplaceOfferAsync(newOffer); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReplaceOfferAsync(Offer offer) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) + { + if (offer == null) + { + throw new ArgumentNullException("offer"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + offer.SelfLink, + offer, + ResourceType.Offer, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse( + await this.UpdateAsync(request, retryPolicyInstance), + OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. + /// + /// The updated to replace the existing resource with. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the updated resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to delete did not exist. + /// + /// + /// + /// + /// + /// r.Id == "user defined type id") + /// .AsEnumerable() + /// .SingleOrDefault(); + /// + /// //Now persist these changes to the database by replacing the original resource + /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Replace, + altLink ?? this.GetLinkForRouting(userDefinedType), + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region Read Impl + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Database resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Database, + databaseLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the document to be read. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// (docLink); + /// ]]> + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); + } + + private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentLink)) + { + throw new ArgumentNullException("documentLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Document, + documentLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link for the DocumentCollection to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionPrivateAsync( + string documentCollectionLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Collection, + documentCollectionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the stored procedure to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.StoredProcedure, + storedProcedureLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Trigger to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggerLink)) + { + throw new ArgumentNullException("triggerLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Trigger, + triggerLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the User Defined Function to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(functionLink)) + { + throw new ArgumentNullException("functionLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedFunction, + functionLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Conflict to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictLink)) + { + throw new ArgumentNullException("conflictLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Conflict, + conflictLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads an from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the Offer to be read. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. + /// + /// + /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about + /// minimum throughput of a Cosmos container (or a database) + /// To retrieve the minimum throughput for a collection/database, use the following sample + /// + /// response = await client.ReadOfferAsync(offer.SelfLink); + /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; + /// ]]> + /// + /// + /// + /// + /// + /// + /// + /// + public Task> ReadOfferAsync(string offerLink) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(offerLink)) + { + throw new ArgumentNullException("offerLink"); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Offer, + offerLink, + null, + AuthorizationTokenType.PrimaryMasterKey)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); + } + } + + /// + /// Reads a as an asynchronous operation. + /// + /// The link for the schema to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Schema are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentSchemaLink)) + { + throw new ArgumentNullException("documentSchemaLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Schema, + documentSchemaLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + await this.AddPartitionKeyInformationAsync(request, options); + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link to the UserDefinedType resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" + /// only the values within the {...} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypeLink)) + { + throw new ArgumentNullException("userDefinedTypeLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.UserDefinedType, + userDefinedTypeLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + /// + /// Reads a from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the Snapshot resource to be read. + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 reading a Snapshot are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource you tried to read did not exist. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. + /// + /// + /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. + /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). + /// ID-based links and SelfLink will both work. + /// The format for is always "/snapshots/{snapshot identifier}" only + /// the values within the {} change depending on which method you wish to use to address the resource. + /// + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(snapshotLink)) + { + throw new ArgumentNullException("snapshotLink"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Read, + ResourceType.Snapshot, + snapshotLink, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); + } + } + + #endregion + + #region ReadFeed Impl + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDatabaseFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDatabaseFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = null; + /// List ids = new List(); + /// do + /// { + /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); + /// foreach (var item in response) + /// { + /// ids.Add(item.Id); + /// } + /// } + /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(partitionKeyRangesLink)) + { + throw new ArgumentNullException("partitionKeyRangesLink"); + } + + return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionsLink)) + { + throw new ArgumentNullException("collectionsLink"); + } + + return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProceduresLink)) + { + throw new ArgumentNullException("storedProceduresLink"); + } + + return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(triggersLink)) + { + throw new ArgumentNullException("triggersLink"); + } + + return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedFunctionsLink)) + { + throw new ArgumentNullException("userDefinedFunctionsLink"); + } + + return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. + /// This takes returns a which will contain an enumerable list of dynamic objects. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// + /// A containing a containing dynamic objects representing the items in the feed. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. + /// This is important becuse a DocumentCollection can contain different kinds of documents. + /// + /// + /// + /// + public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) + { + return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); + } + + private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentsLink)) + { + throw new ArgumentNullException("documentsLink"); + } + + DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); + return new DocumentFeedResponse( + response.Cast(), + response.Count, + response.Headers, + response.UseETagAsContinuation, + response.QueryMetrics, + response.RequestStatistics, + responseLengthBytes: response.ResponseLengthBytes); + } + + /// + /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) + { + return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); + } + + private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(conflictsLink)) + { + throw new ArgumentNullException("conflictsLink"); + } + + return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service + /// as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadOfferAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ReadOffersFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a collection as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionSchemaLink)) + { + throw new ArgumentNullException("documentCollectionSchemaLink"); + } + + return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. + /// + /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a containing the read resource record. + /// + /// If is not set. + /// 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 UserDefinedType are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", + /// new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(userDefinedTypesLink)) + { + throw new ArgumentNullException("userDefinedTypesLink"); + } + + return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); + } + + /// + /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. + /// + /// (Optional) The request options for the request. + /// + /// A containing a which wraps a set of containing the read resource record. + /// + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// + /// response = await client.ReadSnapshotFeedAsync(new FeedOptions + /// { + /// MaxItemCount = 10, + /// RequestContinuation = continuation + /// }); + /// + /// // Append the item count + /// count += response.Count; + /// + /// // Get the continuation so that we know when to stop. + /// continuation = response.ResponseContinuation; + /// } while (!string.IsNullOrEmpty(continuation)); + /// ]]> + /// + /// + /// + /// + /// + /// + internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); + } + + #endregion + + #region Stored procs + /// + /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) + { + return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + default, + procedureParams), + retryPolicyInstance); + } + + /// + /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. + /// + /// The type of the stored procedure's return value. + /// The link to the stored procedure to execute. + /// (Optional) The request options for the request. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. + /// If is not set. + /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. + /// + /// + /// sprocResponse = await client.ExecuteStoredProcedureAsync( + /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", + /// new RequestOptions { PartitionKey = new PartitionKey(1) }, + /// new Player { id="1", name="joe" } , + /// new Player { id="2", name="john" } + /// ); + /// + /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible( + () => this.ExecuteStoredProcedurePrivateAsync( + storedProcedureLink, + options, + retryPolicyInstance, + cancellationToken, + procedureParams), + retryPolicyInstance, + cancellationToken); + } + + private async Task> ExecuteStoredProcedurePrivateAsync( + string storedProcedureLink, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken, + params dynamic[] procedureParams) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(storedProcedureLink)) + { + throw new ArgumentNullException("storedProcedureLink"); + } + + JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); + string storedProcedureInput = serializerSettings == null ? + JsonConvert.SerializeObject(procedureParams) : + JsonConvert.SerializeObject(procedureParams, serializerSettings); + using (MemoryStream storedProcedureInputStream = new MemoryStream()) + { + using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) + { + await writer.WriteAsync(storedProcedureInput); + await writer.FlushAsync(); + storedProcedureInputStream.Position = 0; + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.ExecuteJavaScript, + ResourceType.StoredProcedure, + storedProcedureLink, + storedProcedureInputStream, + AuthorizationTokenType.PrimaryMasterKey, + headers)) + { + request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); + if (options?.PartitionKeyRangeId == null) + { + await this.AddPartitionKeyInformationAsync( + request, + options); + } + + retryPolicyInstance?.OnBeforeSendRequest(request); + + request.SerializerSettings = this.GetSerializerSettingsForRequest(options); + return new StoredProcedureResponse(await this.ExecuteProcedureAsync( + request, + retryPolicyInstance, + cancellationToken), + this.GetSerializerSettingsForRequest(options)); + } + } + } + } + + #endregion + + #region Upsert Impl + /// + /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. + /// + /// The specification for the to upsert. + /// (Optional) The for the request. + /// The that was upserted within a task object representing the service response for the asynchronous operation. + /// If is not set + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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 Database are: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// + /// + /// The example below upserts a new with an Id property of 'MyDatabase' + /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (database == null) + { + throw new ArgumentNullException("database"); + } + + this.ValidateResource(database); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + Paths.Databases_Root, + database, + ResourceType.Database, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ + /// The document object to upsert. + /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. + /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. + /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied + /// + /// + /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. + /// + /// + /// 409Conflict - This means a with an id matching the id field of already existed + /// + /// + /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. + /// + /// + /// 429TooManyRequests - 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. + /// + /// + /// + /// + /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend + /// + /// + /// + /// + /// + /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from + /// + /// + /// + /// + /// + /// A Document can also be a dynamic object + /// + /// + /// + /// + /// + /// Upsert a Document and execute a Pre and Post Trigger + /// + /// { "MyPreTrigger" }, + /// PostTriggerInclude = new List { "MyPostTrigger" } + /// }); + /// } + /// ]]> + /// + /// + /// + /// + /// + /// + public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) + { + // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment + return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); + } + + private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) + { + IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + if (options?.PartitionKey == null) + { + requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( + await this.GetCollectionCacheAsync(NoOpTrace.Singleton), + requestRetryPolicy); + } + + return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( + documentsFeedOrDatabaseLink, + document, + options, + disableAutomaticIdGeneration, + requestRetryPolicy, + cancellationToken), requestRetryPolicy, cancellationToken); + } + + private async Task> UpsertDocumentPrivateAsync( + string documentCollectionLink, + object document, + Documents.Client.RequestOptions options, + bool disableAutomaticIdGeneration, + IDocumentClientRetryPolicy retryPolicyInstance, + CancellationToken cancellationToken) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(documentCollectionLink)) + { + throw new ArgumentNullException("documentCollectionLink"); + } + + if (document == null) + { + throw new ArgumentNullException("document"); + } + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); + Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); + this.ValidateResource(typedDocument); + + if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) + { + typedDocument.Id = Guid.NewGuid().ToString(); + } + + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + documentCollectionLink, + typedDocument, + ResourceType.Document, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None, + this.GetSerializerSettingsForRequest(options))) + { + await this.AddPartitionKeyInformationAsync(request, typedDocument, options); + + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); + } + } + + /// + /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ + /// The object. + /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. + /// + /// + /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) + { + // To be implemented. + throw new NotImplementedException(); + } + + /// + /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// The that was upserted contained within a object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertStoredProcedurePrivateAsync( + string collectionLink, + StoredProcedure storedProcedure, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (storedProcedure == null) + { + throw new ArgumentNullException("storedProcedure"); + } + + this.ValidateResource(storedProcedure); + + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + storedProcedure, + ResourceType.StoredProcedure, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (trigger == null) + { + throw new ArgumentNullException("trigger"); + } + + this.ValidateResource(trigger); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + trigger, + ResourceType.Trigger, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. + /// + /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ + /// The object to upsert. + /// (Optional) Any for this request. + /// A task object representing the service response for the asynchronous operation. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. + /// + /// + /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedFunctionPrivateAsync( + string collectionLink, + UserDefinedFunction function, + Documents.Client.RequestOptions options, + IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(collectionLink)) + { + throw new ArgumentNullException("collectionLink"); + } + + if (function == null) + { + throw new ArgumentNullException("function"); + } + + this.ValidateResource(function); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + collectionLink, + function, + ResourceType.UserDefinedFunction, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + + /// + /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. + /// + /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ + /// The object to upsert. + /// (Optional) The request options for the request. + /// A task object representing the service response for the asynchronous operation which contains the upserted object. + /// If either or is not set. + /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) + /// 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: + /// + /// + /// StatusCodeReason for exception + /// + /// + /// 400BadRequest - This means something was wrong with the request supplied. + /// + /// + /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. + /// + /// + /// 409Conflict - This means a with an id matching the id you supplied already existed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) + { + IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); + return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); + } + + private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + + if (string.IsNullOrEmpty(databaseLink)) + { + throw new ArgumentNullException("databaseLink"); + } + + if (userDefinedType == null) + { + throw new ArgumentNullException("userDefinedType"); + } + + this.ValidateResource(userDefinedType); + INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); + using (DocumentServiceRequest request = DocumentServiceRequest.Create( + OperationType.Upsert, + databaseLink, + userDefinedType, + ResourceType.UserDefinedType, + AuthorizationTokenType.PrimaryMasterKey, + headers, + SerializationFormattingPolicy.None)) + { + return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); + } + } + #endregion + + #region IAuthorizationTokenProvider + + ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType) + { + return this.cosmosAuthorization.GetUserAuthorizationAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType); + } + + ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( + string resourceAddress, + string resourceType, + string requestVerb, + INameValueCollection headers, + AuthorizationTokenType tokenType, + ITrace trace) + { + return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( + resourceAddress, + resourceType, + requestVerb, + headers, + tokenType, + trace); + } + + Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( + DocumentServiceRequest request, + string federationId, + string verb, + string resourceId) + { + return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( + request, + federationId, + verb, + resourceId); + } + + #endregion + + #region Core Implementation + internal Task CreateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpdateAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); + } + + internal Task ReadAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task ReadFeedAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); + } + + internal Task DeleteAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteProcedureAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task ExecuteQueryAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + + internal Task UpsertAsync( + DocumentServiceRequest request, + IDocumentClientRetryPolicy retryPolicy, + CancellationToken cancellationToken = default) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; + return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); + } + #endregion + + /// + /// Read the from the Azure Cosmos DB service as an asynchronous operation. + /// + /// + /// A wrapped in a object. + /// + public Task GetDatabaseAccountAsync() + { + return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); + } + + /// + /// Read the as an asynchronous operation + /// given a specific reginal endpoint url. + /// + /// The reginal url of the serice endpoint. + /// The CancellationToken + /// + /// A wrapped in a object. + /// + Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) + { + return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); + } + + private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) + { + await this.EnsureValidClientAsync(NoOpTrace.Singleton); + if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) + { + async ValueTask CreateRequestMessage() + { + HttpRequestMessage request = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = serviceEndpoint + }; + + INameValueCollection headersCollection = new StoreResponseNameValueCollection(); + await this.cosmosAuthorization.AddAuthorizationHeaderAsync( + headersCollection, + serviceEndpoint, + "GET", + AuthorizationTokenType.PrimaryMasterKey); + + foreach (string key in headersCollection.AllKeys()) + { + request.Headers.Add(key, headersCollection[key]); + } + + return request; + } + + AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, + clientSideRequestStatistics: null); + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; + + if (this.queryPartitionProvider.IsValueCreated) + { + (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); + } + + return databaseAccount; + } + + return null; + } + + #region Private Impl + + /// + /// Certain requests must be routed through gateway even when the client connectivity mode is direct. + /// For e.g., DocumentCollection creation. This method returns the based + /// on the input . + /// + /// Returns to which the request must be sent + internal IStoreModel GetStoreProxy(DocumentServiceRequest request) + { + // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) + // we return the Gateway store model + if (request.UseGatewayMode) + { + return this.GatewayStoreModel; + } + + ResourceType resourceType = request.ResourceType; + OperationType operationType = request.OperationType; + + if (resourceType == ResourceType.Offer || + (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || + resourceType == ResourceType.PartitionKeyRange || + resourceType == ResourceType.Snapshot || + resourceType == ResourceType.ClientEncryptionKey || + (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) + { + return this.GatewayStoreModel; + } + + if (this.isThinClientEnabled + && operationType == OperationType.Read + && resourceType == ResourceType.Database) + { + return this.GatewayStoreModel; + } + + if (operationType == OperationType.Create + || operationType == OperationType.Upsert) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection || + resourceType == ResourceType.Permission) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Delete) + { + if (resourceType == ResourceType.Database || + resourceType == ResourceType.User || + resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else if (operationType == OperationType.Read) + { + if (resourceType == ResourceType.Collection) + { + return this.GatewayStoreModel; + } + else + { + return this.StoreModel; + } + } + else + { + return this.StoreModel; + } + } + + /// + /// The preferred link used in replace operation in SDK. + /// + private string GetLinkForRouting(Documents.Resource resource) + { + // we currently prefer the selflink + return resource.SelfLink ?? resource.AltLink; + } + + internal void EnsureValidOverwrite( + Documents.ConsistencyLevel desiredConsistencyLevel, + OperationType? operationType = null, + ResourceType? resourceType = null) + { + Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; + if (!this.IsValidConsistency( + defaultConsistencyLevel, + desiredConsistencyLevel, + operationType, + resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + desiredConsistencyLevel.ToString(), + defaultConsistencyLevel.ToString())); + } + } + + private bool IsValidConsistency( + Documents.ConsistencyLevel backendConsistency, + Documents.ConsistencyLevel desiredConsistency, + OperationType? operationType, + ResourceType? resourceType) + { + if (this.allowOverrideStrongerConsistency) + { + return true; + } + + return ValidationHelpers.IsValidConsistencyLevelOverwrite( + backendConsistency: backendConsistency, + desiredConsistency: desiredConsistency, + isLocalQuorumConsistency: this.IsLocalQuorumConsistency, + operationType: operationType, + resourceType: resourceType); + } + + private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) + { + // Check if we have a store client factory in input and if we do, do not initialize another store client + // The purpose is to reuse store client factory across all document clients inside compute gateway + if (storeClientFactory != null) + { + this.storeClientFactory = storeClientFactory; + this.isStoreClientFactoryCreatedInternally = false; + } + else + { + // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, + // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. + Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () + { +#if INTERNAL + IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing +#else + IsDistributedTracingEnabled = false +#endif + + }; + + StoreClientFactory newClientFactory = new StoreClientFactory( + this.ConnectionPolicy.ConnectionProtocol, + (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, + this.maxConcurrentConnectionOpenRequests, + this.ConnectionPolicy.UserAgentContainer, + this.eventSource, + null, + this.openConnectionTimeoutInSeconds, + this.idleConnectionTimeoutInSeconds, + this.timerPoolGranularityInSeconds, + this.maxRntbdChannels, + this.rntbdPartitionCount, + this.maxRequestsPerRntbdChannel, + (Documents.PortReuseMode)this.rntbdPortReuseMode, + this.rntbdPortPoolReuseThreshold, + this.rntbdPortPoolBindAttempts, + receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, + sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, + retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), + enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, + rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, + remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, + distributedTracingOptions: distributedTracingOptions, + enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), + chaosInterceptor: this.chaosInterceptor); + + if (this.transportClientHandlerFactory != null) + { + newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); + } + + this.storeClientFactory = newClientFactory; + this.isStoreClientFactoryCreatedInternally = true; + } + + this.AddressResolver = new GlobalAddressResolver( + this.GlobalEndpointManager, + this.PartitionKeyRangeLocation, + this.ConnectionPolicy.ConnectionProtocol, + this, + this.collectionCache, + this.partitionKeyRangeCache, + this.accountServiceConfiguration, + this.ConnectionPolicy, + this.httpClient, + this.storeClientFactory.GetConnectionStateListener(), + this.enableAsyncCacheExceptionNoSharing); + + this.CreateStoreModel(subscribeRntbdStatus: true); + } + + private void CreateStoreModel(bool subscribeRntbdStatus) + { + //EnableReadRequestsFallback, if not explicity set on the connection policy, + //is false if the account's consistency is bounded staleness, + //and true otherwise. + StoreClient storeClient = this.storeClientFactory.CreateStoreClient( + this.AddressResolver, + this.sessionContainer, + this.accountServiceConfiguration, + this, + true, + this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), + !this.enableRntbdChannel, + this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), + true, + enableReplicaValidation: this.isReplicaAddressValidationEnabled, + sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); + + if (subscribeRntbdStatus) + { + storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); + } + + storeClient.SerializerSettings = this.serializerSettings; + + this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); + } + + private void DisableRntbdChannel() + { + Debug.Assert(this.enableRntbdChannel); + this.enableRntbdChannel = false; + this.CreateStoreModel(subscribeRntbdStatus: false); + } + + private async Task InitializeGatewayConfigurationReaderAsync() + { + GatewayAccountReader accountReader = new GatewayAccountReader( + serviceEndpoint: this.ServiceEndpoint, + cosmosAuthorization: this.cosmosAuthorization, + connectionPolicy: this.ConnectionPolicy, + httpClient: this.httpClient, + cancellationToken: this.cancellationTokenSource.Token, + isThinClientEnabled: this.isThinClientEnabled); + + this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); + + await this.accountServiceConfiguration.InitializeAsync(); + AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; + this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; + this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); + } - private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) + internal string GetUserAgentFeatures() { - if (databaseName == null) + int featureFlag = 0; + if (this.ConnectionPolicy.EnablePartitionLevelFailover) { - throw new ArgumentNullException(nameof(databaseName)); + featureFlag += (int)UserAgentFeatureFlags.PerPartitionAutomaticFailover; } - if (collection == null) + if (this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) { - throw new ArgumentNullException(nameof(collection)); + featureFlag += (int)UserAgentFeatureFlags.PerPartitionCircuitBreaker; } - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - using ( - DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Query, - ResourceType.Document, - collection.SelfLink, - AuthorizationTokenType.PrimaryMasterKey)) + if (this.isThinClientEnabled) { - ContainerProperties resolvedCollection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - IReadOnlyList ranges = await this.partitionKeyRangeCache.TryGetOverlappingRangesAsync( - resolvedCollection.ResourceId, - new Range( - PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, - PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, - true, - false), - NoOpTrace.Singleton); - - // In Gateway mode, AddressCache is null - if (this.AddressResolver != null) - { - await this.AddressResolver.OpenAsync(databaseName, resolvedCollection, cancellationToken); - } + featureFlag += (int)UserAgentFeatureFlags.ThinClient; } - } - /// - /// Gets or sets the session object used for session consistency version tracking in the Azure Cosmos DB service. - /// - /// - /// - /// The session object used for version tracking when the consistency level is set to Session. - /// - /// The session object can be saved and shared between two DocumentClient instances within the same AppDomain. - /// - public object Session - { - get + if (ConfigurationManager.IsBinaryEncodingEnabled()) { - return this.sessionContainer; - } - - set - { - SessionContainer container = value as SessionContainer; - if (container == null) - { - throw new ArgumentNullException("value"); - } - - if (!string.Equals(this.ServiceEndpoint.Host, container.HostName, StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - ClientResources.BadSession, - container.HostName, - this.ServiceEndpoint.Host)); - } - - SessionContainer currentSessionContainer = this.sessionContainer as SessionContainer; - if (currentSessionContainer == null) - { - throw new ArgumentNullException(nameof(currentSessionContainer)); - } - - currentSessionContainer.ReplaceCurrrentStateWithStateOf(container); - } - } - - /// - /// Gets or sets the session object used for session consistency version tracking for a specific collection in the Azure Cosmos DB service. - /// - /// Collection for which session token must be retrieved. - /// - /// The session token used for version tracking when the consistency level is set to Session. - /// - /// - /// The session token can be saved and supplied to a request via . - /// - internal string GetSessionToken(string collectionLink) - { - SessionContainer sessionContainerInternal = this.sessionContainer as SessionContainer; - - if (sessionContainerInternal == null) - { - throw new ArgumentNullException(nameof(sessionContainerInternal)); - } - - return sessionContainerInternal.GetSessionToken(collectionLink); - } - - /// - /// Gets the Api type - /// - internal ApiType ApiType - { - get; private set; - } - - internal bool UseMultipleWriteLocations { get; private set; } - - /// - /// Gets the endpoint Uri for the service endpoint from the Azure Cosmos DB service. - /// - /// - /// The Uri for the service endpoint. - /// - /// - public Uri ServiceEndpoint - { - get; - private set; - } - - /// - /// Gets the current write endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri WriteEndpoint - { - get - { - return this.GlobalEndpointManager.WriteEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the current read endpoint chosen based on availability and preference from the Azure Cosmos DB service. - /// - public Uri ReadEndpoint - { - get - { - return this.GlobalEndpointManager.ReadEndpoints.FirstOrDefault(); - } - } - - /// - /// Gets the Connection policy used by the client from the Azure Cosmos DB service. - /// - /// - /// The Connection policy used by the client. - /// - /// - public ConnectionPolicy ConnectionPolicy { get; private set; } - - /// - /// Gets the AuthKey used by the client from the Azure Cosmos DB service. - /// - /// - /// The AuthKey used by the client. - /// - /// - public SecureString AuthKey => throw new NotSupportedException("Please use CosmosAuthorization"); - - /// - /// Gets the configured consistency level of the client from the Azure Cosmos DB service. - /// - /// - /// The configured of the client. - /// - /// - public virtual Documents.ConsistencyLevel ConsistencyLevel - { - get - { -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - TaskHelper.InlineIfPossibleAsync(() => this.EnsureValidClientAsync(NoOpTrace.Singleton), null).Wait(); -#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits - return this.desiredConsistencyLevel.HasValue ? this.desiredConsistencyLevel.Value : - this.accountServiceConfiguration.DefaultConsistencyLevel; - } - } - - /// - /// Returns the account properties available in the service configuration if the client was initialized. - /// - public bool TryGetCachedAccountProperties(out AccountProperties properties) - { - if (this.isSuccessfullyInitialized - && this.accountServiceConfiguration != null - && this.accountServiceConfiguration.AccountProperties != null) - { - properties = this.accountServiceConfiguration.AccountProperties; - return true; - } - - properties = null; - return false; - } - - /// - /// Disposes the client for the Azure Cosmos DB service. - /// - /// - /// - /// - /// - /// - public void Dispose() - { - if (this.isDisposed) - { - return; - } - - if (this.telemetryToServiceHelper != null) - { - this.telemetryToServiceHelper.Dispose(); - this.telemetryToServiceHelper = null; - } - - if (!this.cancellationTokenSource.IsCancellationRequested) - { - this.cancellationTokenSource.Cancel(); - } - - this.cancellationTokenSource.Dispose(); - - if (this.StoreModel != null) - { - this.StoreModel.Dispose(); - this.StoreModel = null; - } - - if (this.storeClientFactory != null) - { - // Dispose only if this store client factory was created and is owned by this instance of document client, otherwise just release the reference - if (this.isStoreClientFactoryCreatedInternally) - { - this.storeClientFactory.Dispose(); - } - - this.storeClientFactory = null; - } - - if (this.AddressResolver != null) - { - this.AddressResolver.Dispose(); - this.AddressResolver = null; - } - - if (this.httpClient != null) - { - try - { - this.httpClient.Dispose(); - } - catch (Exception exception) - { - DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", - exception.Message); - } - - this.httpClient = null; - } - - if (this.cosmosAuthorization != null) - { - this.cosmosAuthorization.Dispose(); - } - - if (this.GlobalEndpointManager != null) - { - this.GlobalEndpointManager.Dispose(); - this.GlobalEndpointManager = null; - } - - if (this.queryPartitionProvider != null && this.queryPartitionProvider.IsValueCreated) - { - this.queryPartitionProvider.Value.Dispose(); - } - - if (this.initTaskCache != null) - { - this.initTaskCache.Dispose(); - this.initTaskCache = null; - } - - DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); - DefaultTrace.Flush(); - - this.isDisposed = true; - } - - //Compatibility mode: - // Allows to specify compatibility mode used by client when making query requests. - // should be removed when application/sql is no longer supported. - internal QueryCompatibilityMode QueryCompatibilityMode { get; set; } - - /// - /// RetryPolicy retries a request when it encounters session unavailable (see ClientRetryPolicy). - /// Once it exhausts all write regions it clears the session container, then it uses ClientCollectionCache - /// to resolves the request's collection name. If it differs from the session container's resource id it - /// explains the session unavailable exception: somebody removed and recreated the collection. In this - /// case we retry once again (with empty session token) otherwise we return the error to the client - /// (see RenameCollectionAwareClientRetryPolicy) - /// - internal virtual IRetryPolicyFactory ResetSessionTokenRetryPolicy { get; private set; } - - /// - /// Gets and sets the IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension StoreModel { get; set; } - - /// - /// Gets and sets the gateway IStoreModel object. - /// - /// - /// Test hook to enable unit test of DocumentClient. - /// - internal IStoreModelExtension GatewayStoreModel { get; set; } - - /// - /// Gets and sets on execute scalar query callback - /// - /// - /// Test hook to enable unit test for scalar queries - /// - internal Action OnExecuteScalarQueryCallback { get; set; } - - internal virtual Task QueryPartitionProvider => this.queryPartitionProvider.Value; - - internal virtual async Task GetDefaultConsistencyLevelAsync() - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - return (ConsistencyLevel)this.accountServiceConfiguration.DefaultConsistencyLevel; - } - - internal Task GetDesiredConsistencyLevelAsync() - { - return Task.FromResult(this.desiredConsistencyLevel); - } - - internal async Task ProcessRequestAsync( - string verb, - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - string testAuthorization = null) // Only for unit-tests - { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (verb == null) - { - throw new ArgumentNullException(nameof(verb)); - } - - (string authorization, string payload) = await this.cosmosAuthorization.GetUserAuthorizationAsync( - request.ResourceAddress, - PathsHelper.GetResourcePath(request.ResourceType), - verb, - request.Headers, - AuthorizationTokenType.PrimaryMasterKey); - - // Unit-test hook - if (testAuthorization != null) - { - payload = testAuthorization; - authorization = testAuthorization; - } - request.Headers[HttpConstants.HttpHeaders.Authorization] = authorization; - - try - { - return await this.ProcessRequestAsync(request, retryPolicyInstance, cancellationToken); - } - catch (DocumentClientException dce) - { - this.cosmosAuthorization.TraceUnauthorized( - dce, - authorization, - payload); - - throw; - } - } - - internal Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - return this.ProcessRequestAsync(request, retryPolicyInstance, NoOpTrace.Singleton, cancellationToken); - } - - internal async Task ProcessRequestAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicyInstance, - ITrace trace, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(trace); - - retryPolicyInstance?.OnBeforeSendRequest(request); - - using (new ActivityScope(Guid.NewGuid())) - { - IStoreModel storeProxy = this.GetStoreProxy(request); - return await storeProxy.ProcessMessageAsync(request, cancellationToken); - } - } - - /// - /// Establishes and Initializes the Rntbd connection to all the backend replica nodes - /// for the given database name and container. - /// - /// A string containing the cosmos database name. - /// A string containing the cosmos container link uri. - /// An instance of the . - internal async Task OpenConnectionsToAllReplicasAsync( - string databaseName, - string containerLinkUri, - CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(databaseName) || - string.IsNullOrEmpty(containerLinkUri)) - { - string resourceName = string.IsNullOrEmpty(databaseName) ? - nameof(databaseName) : - nameof(containerLinkUri); - - throw new ArgumentNullException(resourceName); - } - - if (this.StoreModel != null) - { - try - { - await this.StoreModel.OpenConnectionsToAllReplicasAsync( - databaseName, - containerLinkUri, - cancellationToken); - } - catch (Exception) - { - throw; - } - } - } - - private static string NormalizeAuthorizationPayload(string input) - { - const int expansionBuffer = 12; - StringBuilder builder = new StringBuilder(input.Length + expansionBuffer); - for (int i = 0; i < input.Length; i++) - { - switch (input[i]) - { - case '\n': - builder.Append("\\n"); - break; - case '/': - builder.Append("\\/"); - break; - default: - builder.Append(input[i]); - break; - } - } - - return builder.ToString(); - } - - internal async Task InitilizeFaultInjectionAsync() - { - if (this.chaosInterceptorFactory != null && !this.isChaosInterceptorInititalized) - { - this.isChaosInterceptorInititalized = true; - await this.chaosInterceptorFactory.ConfigureChaosInterceptorAsync(); - } - } - - internal RntbdConnectionConfig RecordTcpSettings(ClientConfigurationTraceDatum clientConfigurationTraceDatum) - { - return new RntbdConnectionConfig(this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.maxRequestsPerRntbdChannel, - this.maxRntbdChannels, - this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - this.rntbdPortReuseMode); - } - - internal virtual async Task EnsureValidClientAsync(ITrace trace) - { - if (this.cancellationTokenSource.IsCancellationRequested || this.isSuccessfullyInitialized) - { - return; - } - - // Trace when the Initialization of client has not been completed. Usually during first call - using (ITrace childTrace = trace.StartChild("Waiting for Initialization of client to complete", TraceComponent.Unknown, Tracing.TraceLevel.Info)) - { - // If the initialization task failed, we should retry initialization. - // We may end up throwing the same exception but this will ensure that we dont have a - // client which is unusable and can resume working if it failed initialization once. - // If we have to reinitialize the client, it needs to happen in thread safe manner so that - // we dont re-initalize the task again for each incoming call. - try - { - this.isSuccessfullyInitialized = await this.initTaskCache.GetAsync( - key: DocumentClient.DefaultInitTaskKey, - singleValueInitFunc: this.initializeTaskFactory, - forceRefresh: (_) => false); - } - catch (DocumentClientException ex) - { - throw Resource.CosmosExceptions.CosmosExceptionFactory.Create( - dce: ex, - trace: trace); - } - catch (Exception e) - { - DefaultTrace.TraceWarning("EnsureValidClientAsync initializeTask failed {0}", e.Message); - childTrace.AddDatum("initializeTask failed", e.Message); - throw; - } - - await this.InitilizeFaultInjectionAsync(); - } - } - - #region Create Impl - /// - /// Creates a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed. - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates(if doesn't exist) or gets(if already exists) a database resource as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the database was newly created(201) or existing database was returned(200) - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. - /// - /// The example below creates a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDatabaseIfNotExistsAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDatabaseIfNotExistsPrivateAsync(database, options), null); - } - - private async Task> CreateDatabaseIfNotExistsPrivateAsync(Documents.Database database, - Documents.Client.RequestOptions options) - { - if (database == null) - { - throw new ArgumentNullException("database"); - } - - // Doing a Read before Create will give us better latency for existing databases - try - { - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDatabaseAsync(database, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(database.Id)); - } - - /// - /// Creates a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to create. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to create the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// Finally, a Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Create a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentAsync(string documentsFeedOrDatabaseLink, - object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, - CancellationToken cancellationToken = default) - { - // This call is to just run CreateDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.CreateDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> CreateDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.CreateDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy); - } - - private async Task> CreateDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Creates a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 collection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionPrivateAsync(databaseLink, documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateDocumentCollectionPrivateAsync( - string databaseLink, - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse( - await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Creates (if doesn't exist) or gets (if already exists) a collection as an asychronous operation in the Azure Cosmos DB service. - /// You can check the status code from the response to determine whether the collection was newly created (201) or existing collection was returned (200). - /// - /// The link of the database to create the collection in. E.g. dbs/db_rid/. - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 DocumentCollection are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateDocumentCollectionIfNotExistsAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.CreateDocumentCollectionIfNotExistsPrivateAsync(databaseLink, documentCollection, options), null); - } - - private async Task> CreateDocumentCollectionIfNotExistsPrivateAsync( - string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options) - { - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - // ReadDatabaseAsync call is needed to support this API that takes databaseLink as a parameter, to be consistent with CreateDocumentCollectionAsync. We need to construct the collectionLink to make - // ReadDocumentCollectionAsync call, in case database selfLink got passed to this API. We cannot simply concat the database selfLink with /colls/{collectionId} to get the collectionLink. - Documents.Database database = await this.ReadDatabaseAsync(databaseLink); - - // Doing a Read before Create will give us better latency for existing collections. - // Also, in emulator case when you hit the max allowed partition count and you use this API for a collection that already exists, - // calling Create will throw 503(max capacity reached) even though the intent of this API is to return the collection if it already exists. - try - { - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - catch (DocumentClientException dce) - { - if (dce.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - } - - try - { - return await this.CreateDocumentCollectionAsync(databaseLink, documentCollection, options); - } - catch (DocumentClientException ex) - { - if (ex.StatusCode != HttpStatusCode.Conflict) - { - throw; - } - } - - // This second Read is to handle the race condition when 2 or more threads have Read the collection and only one succeeds with Create - // so for the remaining ones we should do a Read instead of throwing Conflict exception - return await this.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(database.Id, documentCollection.Id), null); - } - - /// - /// Restores a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link to the source object. - /// The target object. - /// (optional)The point in time to restore. If null, use the latest restorable time. - /// (Optional) The for the request. - /// The task object representing the service response for the asynchronous operation. - internal Task> RestoreDocumentCollectionAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime = null, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.RestoreDocumentCollectionPrivateAsync(sourceDocumentCollectionLink, targetDocumentCollection, restoreTime, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> RestoreDocumentCollectionPrivateAsync(string sourceDocumentCollectionLink, DocumentCollection targetDocumentCollection, DateTimeOffset? restoreTime, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(sourceDocumentCollectionLink)) - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - if (targetDocumentCollection == null) - { - throw new ArgumentNullException("targetDocumentCollection"); - } - - bool isFeed; - string resourceTypeString; - string resourceIdOrFullName; - bool isNameBased; - - string dbsId; - string databaseLink = PathsHelper.GetDatabasePath(sourceDocumentCollectionLink); - if (PathsHelper.TryParsePathSegments(databaseLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - dbsId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - string sourceCollId; - if (PathsHelper.TryParsePathSegments(sourceDocumentCollectionLink, out isFeed, out resourceTypeString, out resourceIdOrFullName, out isNameBased) && isNameBased && !isFeed) - { - string[] segments = resourceIdOrFullName.Split(resourceIdOrFullNameSeparators, StringSplitOptions.RemoveEmptyEntries); - sourceCollId = segments[segments.Length - 1]; - } - else - { - throw new ArgumentNullException("sourceDocumentCollectionLink"); - } - - this.ValidateResource(targetDocumentCollection); - - if (options == null) - { - options = new Documents.Client.RequestOptions(); - } - if (!options.RemoteStorageType.HasValue) - { - options.RemoteStorageType = RemoteStorageType.Standard; - } - options.SourceDatabaseId = dbsId; - options.SourceCollectionId = sourceCollId; - if (restoreTime.HasValue) - { - options.RestorePointInTime = Helpers.ToUnixTime(restoreTime.Value); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - targetDocumentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - // set the session token - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - return collection; - } - } - - /// - /// Get the status of a collection being restored in the Azure Cosmos DB service. - /// - /// The link of the document collection being restored. - /// The task object representing the service response for the asynchronous operation. - internal Task GetDocumentCollectionRestoreStatusAsync(string targetDocumentCollectionLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.GetDocumentCollectionRestoreStatusPrivateAsync(targetDocumentCollectionLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task GetDocumentCollectionRestoreStatusPrivateAsync(string targetDocumentCollectionLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (string.IsNullOrEmpty(targetDocumentCollectionLink)) - { - throw new ArgumentNullException("targetDocumentCollectionLink"); - } - - ResourceResponse response = await this.ReadDocumentCollectionPrivateAsync( - targetDocumentCollectionLink, - new Documents.Client.RequestOptions { PopulateRestoreStatus = true }, - retryPolicyInstance); - string restoreState = response.ResponseHeaders.Get(WFConstants.BackendHeaders.RestoreState); - if (restoreState == null) - { - restoreState = RestoreState.RestoreCompleted.ToString(); - } - - DocumentCollectionRestoreStatus ret = new DocumentCollectionRestoreStatus() - { - State = restoreState - }; - - return ret; - } - - /// - /// Creates a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to create the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// The that was created contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to create the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to create. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to create was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> CreateUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a user defined type object as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to create the user defined type in. E.g. dbs/db_rid/ - /// The object to create. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the created object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Creates a snapshot resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to create. - /// (Optional) The for the request. - /// The that was created within a task object representing the service response for the asynchronous operation. - /// If is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s). - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the snapshot object supplied. It is likely that the resource link specified for the Snapshot was invalid. - /// - /// - /// 409 - /// - /// Conflict - This means a with an id matching the id field of already existed, - /// or there is already a pending snapshot for the specified resource link. - /// - /// - /// - /// - /// - /// The example below creates a new with an Id property of 'MySnapshot'. The ResourceLink indicates that - /// the snapshot should be created for the collection named "myContainer" in the database "myDatabase". - /// This code snippet is intended to be used from within an asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// If you would like to construct a from within a synchronous method then you need to use the following code - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> CreateSnapshotAsync(Snapshot snapshot, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.CreateSnapshotPrivateAsync(snapshot, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> CreateSnapshotPrivateAsync(Snapshot snapshot, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (snapshot == null) - { - throw new ArgumentNullException("snapshot"); - } - - this.ValidateResource(snapshot); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Create, ResourceType.Snapshot); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Create, - Paths.Snapshots_Root, - snapshot, - ResourceType.Snapshot, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.CreateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Delete Impl - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> DeleteDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteDocumentCollectionPrivateAsync(string documentCollectionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/sprocs/sproc_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteStoredProcedurePrivateAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteStoredProcedurePrivateAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/triggers/trigger_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/col_rid/udfs/udf_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> DeleteConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - /// - /// Delete a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the to delete. E.g. snapshots/snapshot_rid/ - /// (Optional) The request options for the request. - /// A containing a which will contain information about the request issued. - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> DeleteSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.DeleteSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> DeleteSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Delete, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Delete, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.DeleteAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Replace Impl - /// - /// Replaces a document collection in the Azure Cosmos DB service as an asynchronous operation. - /// - /// the updated document collection. - /// the request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - public Task> ReplaceDocumentCollectionAsync(DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentCollectionPrivateAsync(documentCollection, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceDocumentCollectionPrivateAsync( - DocumentCollection documentCollection, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (documentCollection == null) - { - throw new ArgumentNullException("documentCollection"); - } - - this.ValidateResource(documentCollection); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(documentCollection), - documentCollection, - ResourceType.Collection, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - ResourceResponse collection = new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - // set the session token - if (collection.Resource != null) - { - this.sessionContainer.SetSessionToken(collection.Resource.ResourceId, collection.Resource.AltLink, collection.Headers); - } - return collection; - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the document to be updated. E.g. dbs/db_rid/colls/col_rid/docs/doc_rid/ - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If either or is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// In this example, instead of using a strongly typed , we will work with our own POCO object and not rely on the dynamic nature of the Document class. - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now dynamically cast doc back to your MyPoco - /// MyPoco poco = (dynamic)doc; - /// - /// //Update some properties of the poco object - /// poco.MyProperty = "updated value"; - /// - /// //Now persist these changes to the database using doc.SelLink and the update poco object - /// Document updated = await client.ReplaceDocumentAsync(doc.SelfLink, poco); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(string documentLink, object document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - // This call is to just run ReplaceDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentInlineAsync(documentLink, document, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReplaceDocumentInlineAsync(string documentLink, object document, Documents.Client.RequestOptions options, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if ((options == null) || (options.PartitionKey == null)) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible( - () => this.ReplaceDocumentPrivateAsync( - documentLink, - document, - options, - requestRetryPolicy, - cancellationToken), - requestRetryPolicy, - cancellationToken); - } - - private Task> ReplaceDocumentPrivateAsync(string documentLink, object document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - return this.ReplaceDocumentPrivateAsync(documentLink, typedDocument, options, retryPolicyInstance, cancellationToken); - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// This example uses and takes advantage of the fact that it is a dynamic object and uses SetProperty to dynamically update properties on the document - /// - /// (collectionLink) - /// .Where(r => r.Id == "doc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// doc.SetPropertyValue("MyProperty", "updated value"); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Document updated = await client.ReplaceDocumentAsync(doc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceDocumentAsync(Document document, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceDocumentPrivateAsync( - this.GetLinkForRouting(document), - document, - options, - retryPolicyInstance, - cancellationToken), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ReplaceDocumentPrivateAsync(string documentLink, Document document, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - this.ValidateResource(document); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - documentLink, - document, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, document, options); - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "sproc id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// sproc.Body = "function () {new javascript body for sproc}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// StoredProcedure updated = await client.ReplaceStoredProcedureAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceStoredProcedureAsync(StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceStoredProcedurePrivateAsync(storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceStoredProcedurePrivateAsync( - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(storedProcedure), - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "trigger id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// trigger.Body = "function () {new javascript body for trigger}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// Trigger updated = await client.ReplaceTriggerAsync(sproc); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceTriggerAsync(Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceTriggerPrivateAsync(trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceTriggerPrivateAsync(Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(trigger), - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "udf id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Update some properties on the found resource - /// udf.Body = "function () {new javascript body for udf}"; - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedFunction updated = await client.ReplaceUserDefinedFunctionAsync(udf); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceUserDefinedFunctionAsync(UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedFunctionPrivateAsync(function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedFunctionPrivateAsync( - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(function), - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// 429TooManyRequests - The replace offer is throttled as the offer scale down operation is attempted within the idle timeout period of 4 hours. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation. - /// - /// - /// - /// - /// - /// r.ResourceLink == "collection selfLink") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Create a new offer with the changed throughput - /// OfferV2 newOffer = new OfferV2(offer, 5000); - /// - /// //Now persist these changes to the database by replacing the original resource - /// Offer updated = await client.ReplaceOfferAsync(newOffer); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReplaceOfferAsync(Offer offer) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceOfferPrivateAsync(offer, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceOfferPrivateAsync(Offer offer, IDocumentClientRetryPolicy retryPolicyInstance) - { - if (offer == null) - { - throw new ArgumentNullException("offer"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - offer.SelfLink, - offer, - ResourceType.Offer, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse( - await this.UpdateAsync(request, retryPolicyInstance), - OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Replaces a in the Azure Cosmos DB service as an asynchronous operation. - /// - /// The updated to replace the existing resource with. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the updated resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to delete did not exist. - /// - /// - /// - /// - /// - /// r.Id == "user defined type id") - /// .AsEnumerable() - /// .SingleOrDefault(); - /// - /// //Now persist these changes to the database by replacing the original resource - /// UserDefinedType updated = await client.ReplaceUserDefinedTypeAsync(userDefinedType); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReplaceUserDefinedTypeAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReplaceUserDefinedTypePrivateAsync(userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReplaceUserDefinedTypePrivateAsync(UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, string altLink = null) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Replace, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Replace, - altLink ?? this.GetLinkForRouting(userDefinedType), - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpdateAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region Read Impl - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Database resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Database if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseAsync(string databaseLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadDatabasePrivateAsync(databaseLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabasePrivateAsync(string databaseLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Database); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Database, - databaseLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Reads a as a generic type T from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the document to be read. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// (docLink); - /// ]]> - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "dbs/{db identifier}/colls/{coll identifier}/docs/{doc identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentAsync(string documentLink, Documents.Client.RequestOptions options = null, CancellationToken cancellationToken = default) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentPrivateAsync(documentLink, options, retryPolicyInstance, cancellationToken), retryPolicyInstance, cancellationToken); - } - - private async Task> ReadDocumentPrivateAsync(string documentLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentLink)) - { - throw new ArgumentNullException("documentLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Document); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Document, - documentLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new DocumentResponse(await this.ReadAsync(request, retryPolicyInstance, cancellationToken), this.GetSerializerSettingsForRequest(options)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link for the DocumentCollection to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the DocumentCollection if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionAsync(string documentCollectionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionPrivateAsync(documentCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionPrivateAsync( - string documentCollectionLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Collection); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Collection, - documentCollectionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the stored procedure to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Stored Procedure if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/sprocs/{sproc identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureAsync(storedProcedureLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.StoredProcedure, - storedProcedureLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Trigger to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Trigger if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/triggers/{trigger identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerAsync(string triggerLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerPrivateAsync(triggerLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerPrivateAsync(string triggerLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggerLink)) - { - throw new ArgumentNullException("triggerLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Trigger, - triggerLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the User Defined Function to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the User Defined Function if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/udfs/{udf identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionAsync(string functionLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionPrivateAsync(functionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionPrivateAsync(string functionLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(functionLink)) - { - throw new ArgumentNullException("functionLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedFunction, - functionLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Conflict to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Conflict if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{collectioon identifier}/conflicts/{conflict identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - public Task> ReadConflictAsync(string conflictLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadConflictPrivateAsync(conflictLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadConflictPrivateAsync(string conflictLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictLink)) - { - throw new ArgumentNullException("conflictLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Conflict); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Conflict, - conflictLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads an from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the Offer to be read. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// For an Offer, id is always generated internally by the system when the linked resource is created. id and _rid are always the same for Offer. - /// - /// - /// Refer to https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-provision-container-throughput to learn more about - /// minimum throughput of a Cosmos container (or a database) - /// To retrieve the minimum throughput for a collection/database, use the following sample - /// - /// response = await client.ReadOfferAsync(offer.SelfLink); - /// string minimumRUsForCollection = readResponse.Headers["x-ms-cosmos-min-throughput"]; - /// ]]> - /// - /// - /// - /// - /// - /// - /// - /// - public Task> ReadOfferAsync(string offerLink) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferPrivateAsync(offerLink, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferPrivateAsync(string offerLink, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(offerLink)) - { - throw new ArgumentNullException("offerLink"); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Offer, - offerLink, - null, - AuthorizationTokenType.PrimaryMasterKey)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance), OfferTypeResolver.ResponseOfferTypeResolver); - } - } - - /// - /// Reads a as an asynchronous operation. - /// - /// The link for the schema to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Schema are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Document if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/colls/{coll identifier}/schema/{schema identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaAsync(string documentSchemaLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSchemaPrivateAsync(documentSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaPrivateAsync(string documentSchemaLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentSchemaLink)) - { - throw new ArgumentNullException("documentSchemaLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Schema); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Schema, - documentSchemaLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - await this.AddPartitionKeyInformationAsync(request, options); - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link to the UserDefinedType resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Database. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown user defined type ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the UserDefinedType if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/dbs/{db identifier}/udts/{user defined type identifier}" - /// only the values within the {...} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypePrivateAsync(userDefinedTypeLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypePrivateAsync(string userDefinedTypeLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypeLink)) - { - throw new ArgumentNullException("userDefinedTypeLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.UserDefinedType, - userDefinedTypeLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - /// - /// Reads a from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the Snapshot resource to be read. - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 reading a Snapshot are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource you tried to read did not exist. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Doing a read of a resource is the most efficient way to get a resource from the Azure Cosmos DB service. If you know the resource's ID, do a read instead of a query by ID. - /// - /// - /// The example shown uses ID-based links, where the link is composed of the ID properties used when the resources were created. - /// You can still use the property of the Snapshot if you prefer. A self-link is a URI for a resource that is made up of Resource Identifiers (or the _rid properties). - /// ID-based links and SelfLink will both work. - /// The format for is always "/snapshots/{snapshot identifier}" only - /// the values within the {} change depending on which method you wish to use to address the resource. - /// - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotAsync(string snapshotLink, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSnapshotPrivateAsync(snapshotLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotPrivateAsync(string snapshotLink, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(snapshotLink)) - { - throw new ArgumentNullException("snapshotLink"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Read, ResourceType.Snapshot); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Read, - ResourceType.Snapshot, - snapshotLink, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - return new ResourceResponse(await this.ReadAsync(request, retryPolicyInstance)); - } - } - - #endregion - - #region ReadFeed Impl - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDatabaseFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDatabaseFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDatabaseFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDatabaseFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateDatabaseFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The link of the resources to be read, or owner collection link, SelfLink or AltLink. E.g. /dbs/db_rid/colls/coll_rid/pkranges - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = null; - /// List ids = new List(); - /// do - /// { - /// response = await client.ReadPartitionKeyRangeFeedAsync(collection.SelfLink, new FeedOptions { MaxItemCount = 1000 }); - /// foreach (var item in response) - /// { - /// ids.Add(item.Id); - /// } - /// } - /// while (!string.IsNullOrEmpty(response.ResponseContinuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadPartitionKeyRangeFeedAsync(string partitionKeyRangesOrCollectionLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadPartitionKeyRangeFeedPrivateAsync(partitionKeyRangesOrCollectionLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadPartitionKeyRangeFeedPrivateAsync(string partitionKeyRangesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(partitionKeyRangesLink)) - { - throw new ArgumentNullException("partitionKeyRangesLink"); - } - - return await this.CreatePartitionKeyRangeFeedReader(partitionKeyRangesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentCollectionFeedAsync("/dbs/db_rid/colls/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadDocumentCollectionFeedAsync(string collectionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadDocumentCollectionFeedPrivateAsync(collectionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadDocumentCollectionFeedPrivateAsync(string collectionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionsLink)) - { - throw new ArgumentNullException("collectionsLink"); - } - - return await this.CreateDocumentCollectionFeedReader(collectionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/sprocs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadStoredProcedureFeedAsync("/dbs/db_rid/colls/col_rid/sprocs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadStoredProcedureFeedAsync(string storedProceduresLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadStoredProcedureFeedPrivateAsync(storedProceduresLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadStoredProcedureFeedPrivateAsync(string storedProceduresLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProceduresLink)) - { - throw new ArgumentNullException("storedProceduresLink"); - } - - return await this.CreateStoredProcedureFeedReader(storedProceduresLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/triggers/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadTriggerFeedAsync("/dbs/db_rid/colls/col_rid/triggers/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadTriggerFeedAsync(string triggersLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadTriggerFeedPrivateAsync(triggersLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadTriggerFeedPrivateAsync(string triggersLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(triggersLink)) - { - throw new ArgumentNullException("triggersLink"); - } - - return await this.CreateTriggerFeedReader(triggersLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/col_rid/udfs/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedFunctionFeedAsync("/dbs/db_rid/colls/col_rid/udfs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadUserDefinedFunctionFeedAsync(string userDefinedFunctionsLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedFunctionFeedPrivateAsync(userDefinedFunctionsLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedFunctionFeedPrivateAsync(string userDefinedFunctionsLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedFunctionsLink)) - { - throw new ArgumentNullException("userDefinedFunctionsLink"); - } - - return await this.CreateUserDefinedFunctionFeedReader(userDefinedFunctionsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of documents for a specified collection from the Azure Cosmos DB service. - /// This takes returns a which will contain an enumerable list of dynamic objects. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/docs/ - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// - /// A containing a containing dynamic objects representing the items in the feed. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadDocumentFeedAsync("/dbs/db_rid/colls/coll_rid/docs/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// Instead of DoucmentFeedResponse{Document} this method takes advantage of dynamic objects in .NET. This way a single feed result can contain any kind of Document, or POCO object. - /// This is important becuse a DocumentCollection can contain different kinds of documents. - /// - /// - /// - /// - public Task> ReadDocumentFeedAsync(string documentsLink, FeedOptions options = null, CancellationToken cancellationToken = default) - { - return TaskHelper.InlineIfPossible(() => this.ReadDocumentFeedInlineAsync(documentsLink, options, cancellationToken), null, cancellationToken); - } - - private async Task> ReadDocumentFeedInlineAsync(string documentsLink, FeedOptions options, CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentsLink)) - { - throw new ArgumentNullException("documentsLink"); - } - - DocumentFeedResponse response = await this.CreateDocumentFeedReader(documentsLink, options).ExecuteNextAsync(cancellationToken); - return new DocumentFeedResponse( - response.Cast(), - response.Count, - response.Headers, - response.UseETagAsContinuation, - response.QueryMetrics, - response.RequestStatistics, - responseLengthBytes: response.ResponseLengthBytes); - } - - /// - /// Reads the feed (sequence) of for a collection from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/conflicts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadConflictAsync("/dbs/db_rid/colls/coll_rid/conflicts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadConflictFeedAsync(string conflictsLink, FeedOptions options = null) - { - return TaskHelper.InlineIfPossible(() => this.ReadConflictFeedInlineAsync(conflictsLink, options), null); - } - - private async Task> ReadConflictFeedInlineAsync(string conflictsLink, FeedOptions options) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(conflictsLink)) - { - throw new ArgumentNullException("conflictsLink"); - } - - return await this.CreateConflictFeedReader(conflictsLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service - /// as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadOfferAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ReadOffersFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadOfferFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadOfferFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateOfferFeedReader(options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a collection as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/colls/coll_rid/schemas - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserFeedAsync("/dbs/db_rid/colls/coll_rid/schemas", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSchemaFeedAsync(string documentCollectionSchemaLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.ReadSchemaFeedPrivateAsync(documentCollectionSchemaLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSchemaFeedPrivateAsync(string documentCollectionSchemaLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionSchemaLink)) - { - throw new ArgumentNullException("documentCollectionSchemaLink"); - } - - return await this.CreateSchemaFeedReader(documentCollectionSchemaLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database from the Azure Cosmos DB service as an asynchronous operation. - /// - /// The SelfLink of the resources to be read. E.g. /dbs/db_rid/udts/ - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a containing the read resource record. - /// - /// If is not set. - /// 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 UserDefinedType are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 404NotFound - This means the resource feed you tried to read did not exist. Check the parent rids are correct. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadUserDefinedTypeFeedAsync("/dbs/db_rid/udts/", - /// new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadUserDefinedTypeFeedAsync(string userDefinedTypesLink, FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadUserDefinedTypeFeedPrivateAsync(userDefinedTypesLink, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadUserDefinedTypeFeedPrivateAsync(string userDefinedTypesLink, FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(userDefinedTypesLink)) - { - throw new ArgumentNullException("userDefinedTypesLink"); - } - - return await this.CreateUserDefinedTypeFeedReader(userDefinedTypesLink, options).ExecuteNextAsync(); - } - - /// - /// Reads the feed (sequence) of for a database account from the Azure Cosmos DB service as an asynchronous operation. - /// - /// (Optional) The request options for the request. - /// - /// A containing a which wraps a set of containing the read resource record. - /// - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// - /// response = await client.ReadSnapshotFeedAsync(new FeedOptions - /// { - /// MaxItemCount = 10, - /// RequestContinuation = continuation - /// }); - /// - /// // Append the item count - /// count += response.Count; - /// - /// // Get the continuation so that we know when to stop. - /// continuation = response.ResponseContinuation; - /// } while (!string.IsNullOrEmpty(continuation)); - /// ]]> - /// - /// - /// - /// - /// - /// - internal Task> ReadSnapshotFeedAsync(FeedOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ReadSnapshotFeedPrivateAsync(options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> ReadSnapshotFeedPrivateAsync(FeedOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - return await this.CreateSnapshotFeedReader(options).ExecuteNextAsync(); - } - - #endregion - - #region Stored procs - /// - /// Executes a stored procedure against a collection as an asynchronous operation in the Azure Cosmos DB service. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, params dynamic[] procedureParams) - { - return this.ExecuteStoredProcedureAsync(storedProcedureLink, null, default, procedureParams); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - default, - procedureParams), - retryPolicyInstance); - } - - /// - /// Executes a stored procedure against a partitioned collection in the Azure Cosmos DB service as an asynchronous operation, specifiying a target partition. - /// - /// The type of the stored procedure's return value. - /// The link to the stored procedure to execute. - /// (Optional) The request options for the request. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// (Optional) An array of dynamic objects representing the parameters for the stored procedure. - /// If is not set. - /// The task object representing the service response for the asynchronous operation which would contain any response set in the stored procedure. - /// - /// - /// sprocResponse = await client.ExecuteStoredProcedureAsync( - /// "/dbs/db_rid/colls/col_rid/sprocs/sproc_rid/", - /// new RequestOptions { PartitionKey = new PartitionKey(1) }, - /// new Player { id="1", name="joe" } , - /// new Player { id="2", name="john" } - /// ); - /// - /// if (sprocResponse.Response) Console.WriteLine("Congrats, the stored procedure did some stuff"); - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> ExecuteStoredProcedureAsync(string storedProcedureLink, Documents.Client.RequestOptions options, CancellationToken cancellationToken, params dynamic[] procedureParams) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible( - () => this.ExecuteStoredProcedurePrivateAsync( - storedProcedureLink, - options, - retryPolicyInstance, - cancellationToken, - procedureParams), - retryPolicyInstance, - cancellationToken); - } - - private async Task> ExecuteStoredProcedurePrivateAsync( - string storedProcedureLink, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken, - params dynamic[] procedureParams) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(storedProcedureLink)) - { - throw new ArgumentNullException("storedProcedureLink"); - } - - JsonSerializerSettings serializerSettings = this.GetSerializerSettingsForRequest(options); - string storedProcedureInput = serializerSettings == null ? - JsonConvert.SerializeObject(procedureParams) : - JsonConvert.SerializeObject(procedureParams, serializerSettings); - using (MemoryStream storedProcedureInputStream = new MemoryStream()) - { - using (StreamWriter writer = new StreamWriter(storedProcedureInputStream)) - { - await writer.WriteAsync(storedProcedureInput); - await writer.FlushAsync(); - storedProcedureInputStream.Position = 0; - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.ExecuteJavaScript, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.ExecuteJavaScript, - ResourceType.StoredProcedure, - storedProcedureLink, - storedProcedureInputStream, - AuthorizationTokenType.PrimaryMasterKey, - headers)) - { - request.Headers[HttpConstants.HttpHeaders.XDate] = Rfc1123DateTimeCache.UtcNow(); - if (options?.PartitionKeyRangeId == null) - { - await this.AddPartitionKeyInformationAsync( - request, - options); - } - - retryPolicyInstance?.OnBeforeSendRequest(request); - - request.SerializerSettings = this.GetSerializerSettingsForRequest(options); - return new StoredProcedureResponse(await this.ExecuteProcedureAsync( - request, - retryPolicyInstance, - cancellationToken), - this.GetSerializerSettingsForRequest(options)); - } - } - } - } - - #endregion - - #region Upsert Impl - /// - /// Upserts a database resource as an asychronous operation in the Azure Cosmos DB service. - /// - /// The specification for the to upsert. - /// (Optional) The for the request. - /// The that was upserted within a task object representing the service response for the asynchronous operation. - /// If is not set - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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 Database are: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the database object supplied. It is likely that an id was not supplied for the new Database. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// - /// - /// The example below upserts a new with an Id property of 'MyDatabase' - /// This code snippet is intended to be used from within an Asynchronous method as it uses the await keyword - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDatabaseAsync(Documents.Database database, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertDatabasePrivateAsync(database, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertDatabasePrivateAsync(Documents.Database database, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (database == null) - { - throw new ArgumentNullException("database"); - } - - this.ValidateResource(database); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Database); - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - Paths.Databases_Root, - database, - ResourceType.Database, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a Document as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the document in. E.g. dbs/db_rid/colls/coll_rid/ - /// The document object to upsert. - /// (Optional) Any request options you wish to set. E.g. Specifying a Trigger to execute when creating the document. - /// (Optional) Disables the automatic id generation, If this is True the system will throw an exception if the id property is missing from the Document. - /// (Optional) A that can be used by other objects or threads to receive notice of cancellation. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the document supplied. It is likely that was true and an id was not supplied - /// - /// - /// 403Forbidden - This likely means the collection in to which you were trying to upsert the document is full. - /// - /// - /// 409Conflict - This means a with an id matching the id field of already existed - /// - /// - /// 413RequestEntityTooLarge - This means the exceeds the current max entity size. Consult documentation for limits and quotas. - /// - /// - /// 429TooManyRequests - 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. - /// - /// - /// - /// - /// Azure Cosmos DB supports a number of different ways to work with documents. A document can extend - /// - /// - /// - /// - /// - /// A document can be any POCO object that can be serialized to JSON, even if it doesn't extend from - /// - /// - /// - /// - /// - /// A Document can also be a dynamic object - /// - /// - /// - /// - /// - /// Upsert a Document and execute a Pre and Post Trigger - /// - /// { "MyPreTrigger" }, - /// PostTriggerInclude = new List { "MyPostTrigger" } - /// }); - /// } - /// ]]> - /// - /// - /// - /// - /// - /// - public Task> UpsertDocumentAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options = null, bool disableAutomaticIdGeneration = false, CancellationToken cancellationToken = default) - { - // This call is to just run UpsertDocumentInlineAsync in a SynchronizationContext aware environment - return TaskHelper.InlineIfPossible(() => this.UpsertDocumentInlineAsync(documentsFeedOrDatabaseLink, document, options, disableAutomaticIdGeneration, cancellationToken), null, cancellationToken); - } - - private async Task> UpsertDocumentInlineAsync(string documentsFeedOrDatabaseLink, object document, Documents.Client.RequestOptions options, bool disableAutomaticIdGeneration, CancellationToken cancellationToken) - { - IDocumentClientRetryPolicy requestRetryPolicy = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - if (options?.PartitionKey == null) - { - requestRetryPolicy = new PartitionKeyMismatchRetryPolicy( - await this.GetCollectionCacheAsync(NoOpTrace.Singleton), - requestRetryPolicy); - } - - return await TaskHelper.InlineIfPossible(() => this.UpsertDocumentPrivateAsync( - documentsFeedOrDatabaseLink, - document, - options, - disableAutomaticIdGeneration, - requestRetryPolicy, - cancellationToken), requestRetryPolicy, cancellationToken); - } - - private async Task> UpsertDocumentPrivateAsync( - string documentCollectionLink, - object document, - Documents.Client.RequestOptions options, - bool disableAutomaticIdGeneration, - IDocumentClientRetryPolicy retryPolicyInstance, - CancellationToken cancellationToken) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(documentCollectionLink)) - { - throw new ArgumentNullException("documentCollectionLink"); - } - - if (document == null) - { - throw new ArgumentNullException("document"); - } - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Document); - Document typedDocument = Document.FromObject(document, this.GetSerializerSettingsForRequest(options)); - this.ValidateResource(typedDocument); - - if (string.IsNullOrEmpty(typedDocument.Id) && !disableAutomaticIdGeneration) - { - typedDocument.Id = Guid.NewGuid().ToString(); - } - - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - documentCollectionLink, - typedDocument, - ResourceType.Document, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None, - this.GetSerializerSettingsForRequest(options))) - { - await this.AddPartitionKeyInformationAsync(request, typedDocument, options); - - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance, cancellationToken)); - } - } - - /// - /// Upserts a collection as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the database to upsert the collection in. E.g. dbs/db_rid/ - /// The object. - /// (Optional) Any you wish to provide when creating a Collection. E.g. RequestOptions.OfferThroughput = 400. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new collection. - /// - /// - /// 403Forbidden - This means you attempted to exceed your quota for collections. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertDocumentCollectionAsync(string databaseLink, DocumentCollection documentCollection, Documents.Client.RequestOptions options = null) - { - // To be implemented. - throw new NotImplementedException(); - } - - /// - /// Upserts a stored procedure as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the collection to upsert the stored procedure in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// The that was upserted contained within a object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the stored procedure or the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of stored procedures for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertStoredProcedureAsync(string collectionLink, StoredProcedure storedProcedure, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertStoredProcedurePrivateAsync(collectionLink, storedProcedure, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertStoredProcedurePrivateAsync( - string collectionLink, - StoredProcedure storedProcedure, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (storedProcedure == null) - { - throw new ArgumentNullException("storedProcedure"); - } - - this.ValidateResource(storedProcedure); - - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.StoredProcedure); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - storedProcedure, - ResourceType.StoredProcedure, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a trigger as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the trigger in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new trigger or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of triggers for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertTriggerAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertTriggerPrivateAsync(collectionLink, trigger, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertTriggerPrivateAsync(string collectionLink, Trigger trigger, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (trigger == null) - { - throw new ArgumentNullException("trigger"); - } - - this.ValidateResource(trigger); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.Trigger); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - trigger, - ResourceType.Trigger, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined function as an asychronous operation in the Azure Cosmos DB service. - /// - /// The link of the to upsert the user defined function in. E.g. dbs/db_rid/colls/col_rid/ - /// The object to upsert. - /// (Optional) Any for this request. - /// A task object representing the service response for the asynchronous operation. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. It is likely that an Id was not supplied for the new user defined function or that the Body was malformed. - /// - /// - /// 403Forbidden - You have reached your quota of user defined functions for the collection supplied. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// 413RequestEntityTooLarge - This means the body of the you tried to upsert was too large. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Task> UpsertUserDefinedFunctionAsync(string collectionLink, UserDefinedFunction function, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedFunctionPrivateAsync(collectionLink, function, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedFunctionPrivateAsync( - string collectionLink, - UserDefinedFunction function, - Documents.Client.RequestOptions options, - IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(collectionLink)) - { - throw new ArgumentNullException("collectionLink"); - } - - if (function == null) - { - throw new ArgumentNullException("function"); - } - - this.ValidateResource(function); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedFunction); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - collectionLink, - function, - ResourceType.UserDefinedFunction, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - - /// - /// Upserts a user defined type object in the Azure Cosmos DB service as an asychronous operation. - /// - /// The link of the database to upsert the user defined type in. E.g. dbs/db_rid/ - /// The object to upsert. - /// (Optional) The request options for the request. - /// A task object representing the service response for the asynchronous operation which contains the upserted object. - /// If either or is not set. - /// Represents a consolidation of failures that occured during async processing. Look within InnerExceptions to find the actual exception(s) - /// 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: - /// - /// - /// StatusCodeReason for exception - /// - /// - /// 400BadRequest - This means something was wrong with the request supplied. - /// - /// - /// 403Forbidden - You have reached your quota of user defined type objects for this database. Contact support to have this quota increased. - /// - /// - /// 409Conflict - This means a with an id matching the id you supplied already existed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal Task> UpsertUserDefinedTypeAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options = null) - { - IDocumentClientRetryPolicy retryPolicyInstance = this.ResetSessionTokenRetryPolicy.GetRequestPolicy(); - return TaskHelper.InlineIfPossible(() => this.UpsertUserDefinedTypePrivateAsync(databaseLink, userDefinedType, options, retryPolicyInstance), retryPolicyInstance); - } - - private async Task> UpsertUserDefinedTypePrivateAsync(string databaseLink, UserDefinedType userDefinedType, Documents.Client.RequestOptions options, IDocumentClientRetryPolicy retryPolicyInstance) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - - if (string.IsNullOrEmpty(databaseLink)) - { - throw new ArgumentNullException("databaseLink"); - } - - if (userDefinedType == null) - { - throw new ArgumentNullException("userDefinedType"); - } - - this.ValidateResource(userDefinedType); - INameValueCollection headers = this.GetRequestHeaders(options, OperationType.Upsert, ResourceType.UserDefinedType); - using (DocumentServiceRequest request = DocumentServiceRequest.Create( - OperationType.Upsert, - databaseLink, - userDefinedType, - ResourceType.UserDefinedType, - AuthorizationTokenType.PrimaryMasterKey, - headers, - SerializationFormattingPolicy.None)) - { - return new ResourceResponse(await this.UpsertAsync(request, retryPolicyInstance)); - } - } - #endregion - - #region IAuthorizationTokenProvider - - ValueTask<(string token, string payload)> IAuthorizationTokenProvider.GetUserAuthorizationAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType) - { - return this.cosmosAuthorization.GetUserAuthorizationAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType); - } - - ValueTask ICosmosAuthorizationTokenProvider.GetUserAuthorizationTokenAsync( - string resourceAddress, - string resourceType, - string requestVerb, - INameValueCollection headers, - AuthorizationTokenType tokenType, - ITrace trace) - { - return this.cosmosAuthorization.GetUserAuthorizationTokenAsync( - resourceAddress, - resourceType, - requestVerb, - headers, - tokenType, - trace); - } - - Task IAuthorizationTokenProvider.AddSystemAuthorizationHeaderAsync( - DocumentServiceRequest request, - string federationId, - string verb, - string resourceId) - { - return this.cosmosAuthorization.AddSystemAuthorizationHeaderAsync( - request, - federationId, - verb, - resourceId); - } - - #endregion - - #region Core Implementation - internal Task CreateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpdateAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Put, request, retryPolicy, cancellationToken); - } - - internal Task ReadAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task ReadFeedAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Get, request, retryPolicy, cancellationToken); - } - - internal Task DeleteAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Delete, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteProcedureAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task ExecuteQueryAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - - internal Task UpsertAsync( - DocumentServiceRequest request, - IDocumentClientRetryPolicy retryPolicy, - CancellationToken cancellationToken = default) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - request.Headers[HttpConstants.HttpHeaders.IsUpsert] = bool.TrueString; - return this.ProcessRequestAsync(HttpConstants.HttpMethods.Post, request, retryPolicy, cancellationToken); - } - #endregion - - /// - /// Read the from the Azure Cosmos DB service as an asynchronous operation. - /// - /// - /// A wrapped in a object. - /// - public Task GetDatabaseAccountAsync() - { - return TaskHelper.InlineIfPossible(() => this.GetDatabaseAccountPrivateAsync(this.ReadEndpoint), this.ResetSessionTokenRetryPolicy.GetRequestPolicy()); - } - - /// - /// Read the as an asynchronous operation - /// given a specific reginal endpoint url. - /// - /// The reginal url of the serice endpoint. - /// The CancellationToken - /// - /// A wrapped in a object. - /// - Task IDocumentClientInternal.GetDatabaseAccountInternalAsync(Uri serviceEndpoint, CancellationToken cancellationToken) - { - return this.GetDatabaseAccountPrivateAsync(serviceEndpoint, cancellationToken); - } - - private async Task GetDatabaseAccountPrivateAsync(Uri serviceEndpoint, CancellationToken cancellationToken = default) - { - await this.EnsureValidClientAsync(NoOpTrace.Singleton); - if (this.GatewayStoreModel is GatewayStoreModel gatewayModel) - { - async ValueTask CreateRequestMessage() - { - HttpRequestMessage request = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = serviceEndpoint - }; - - INameValueCollection headersCollection = new StoreResponseNameValueCollection(); - await this.cosmosAuthorization.AddAuthorizationHeaderAsync( - headersCollection, - serviceEndpoint, - "GET", - AuthorizationTokenType.PrimaryMasterKey); - - foreach (string key in headersCollection.AllKeys()) - { - request.Headers.Add(key, headersCollection[key]); - } - - return request; - } - - AccountProperties databaseAccount = await gatewayModel.GetDatabaseAccountAsync(CreateRequestMessage, - clientSideRequestStatistics: null); - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && databaseAccount.EnableMultipleWriteLocations; - - if (this.queryPartitionProvider.IsValueCreated) - { - (await this.QueryPartitionProvider).Update(databaseAccount.QueryEngineConfiguration); - } - - return databaseAccount; - } - - return null; - } - - #region Private Impl - - /// - /// Certain requests must be routed through gateway even when the client connectivity mode is direct. - /// For e.g., DocumentCollection creation. This method returns the based - /// on the input . - /// - /// Returns to which the request must be sent - internal IStoreModel GetStoreProxy(DocumentServiceRequest request) - { - // If a request is configured to always use Gateway mode(in some cases when targeting .NET Core) - // we return the Gateway store model - if (request.UseGatewayMode) - { - return this.GatewayStoreModel; - } - - ResourceType resourceType = request.ResourceType; - OperationType operationType = request.OperationType; - - if (resourceType == ResourceType.Offer || - (resourceType.IsScript() && operationType != OperationType.ExecuteJavaScript) || - resourceType == ResourceType.PartitionKeyRange || - resourceType == ResourceType.Snapshot || - resourceType == ResourceType.ClientEncryptionKey || - (resourceType == ResourceType.PartitionKey && operationType == OperationType.Delete)) - { - return this.GatewayStoreModel; - } - - if (this.isThinClientEnabled - && operationType == OperationType.Read - && resourceType == ResourceType.Database) - { - return this.GatewayStoreModel; - } - - if (operationType == OperationType.Create - || operationType == OperationType.Upsert) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection || - resourceType == ResourceType.Permission) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Delete) - { - if (resourceType == ResourceType.Database || - resourceType == ResourceType.User || - resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if ((operationType == OperationType.Replace) || (operationType == OperationType.CollectionTruncate)) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else if (operationType == OperationType.Read) - { - if (resourceType == ResourceType.Collection) - { - return this.GatewayStoreModel; - } - else - { - return this.StoreModel; - } - } - else - { - return this.StoreModel; - } - } - - /// - /// The preferred link used in replace operation in SDK. - /// - private string GetLinkForRouting(Documents.Resource resource) - { - // we currently prefer the selflink - return resource.SelfLink ?? resource.AltLink; - } - - internal void EnsureValidOverwrite( - Documents.ConsistencyLevel desiredConsistencyLevel, - OperationType? operationType = null, - ResourceType? resourceType = null) - { - Documents.ConsistencyLevel defaultConsistencyLevel = this.accountServiceConfiguration.DefaultConsistencyLevel; - if (!this.IsValidConsistency( - defaultConsistencyLevel, - desiredConsistencyLevel, - operationType, - resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - desiredConsistencyLevel.ToString(), - defaultConsistencyLevel.ToString())); - } - } - - private bool IsValidConsistency( - Documents.ConsistencyLevel backendConsistency, - Documents.ConsistencyLevel desiredConsistency, - OperationType? operationType, - ResourceType? resourceType) - { - if (this.allowOverrideStrongerConsistency) - { - return true; - } - - return ValidationHelpers.IsValidConsistencyLevelOverwrite( - backendConsistency: backendConsistency, - desiredConsistency: desiredConsistency, - isLocalQuorumConsistency: this.IsLocalQuorumConsistency, - operationType: operationType, - resourceType: resourceType); - } - - private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory) - { - // Check if we have a store client factory in input and if we do, do not initialize another store client - // The purpose is to reuse store client factory across all document clients inside compute gateway - if (storeClientFactory != null) - { - this.storeClientFactory = storeClientFactory; - this.isStoreClientFactoryCreatedInternally = false; - } - else - { - // It is decided to switch this feature off completely for external users but keep it unchanged for internal users, - // due to the nature of information, we are collecting here. RNTBD is internal protocol and we do not expose it to the customers. - Documents.Telemetry.DistributedTracingOptions distributedTracingOptions = new () - { -#if INTERNAL - IsDistributedTracingEnabled = !this.cosmosClientTelemetryOptions.DisableDistributedTracing -#else - IsDistributedTracingEnabled = false -#endif - - }; - - StoreClientFactory newClientFactory = new StoreClientFactory( - this.ConnectionPolicy.ConnectionProtocol, - (int)this.ConnectionPolicy.RequestTimeout.TotalSeconds, - this.maxConcurrentConnectionOpenRequests, - this.ConnectionPolicy.UserAgentContainer, - this.eventSource, - null, - this.openConnectionTimeoutInSeconds, - this.idleConnectionTimeoutInSeconds, - this.timerPoolGranularityInSeconds, - this.maxRntbdChannels, - this.rntbdPartitionCount, - this.maxRequestsPerRntbdChannel, - (Documents.PortReuseMode)this.rntbdPortReuseMode, - this.rntbdPortPoolReuseThreshold, - this.rntbdPortPoolBindAttempts, - receiveHangDetectionTimeSeconds: this.rntbdReceiveHangDetectionTimeSeconds, - sendHangDetectionTimeSeconds: this.rntbdSendHangDetectionTimeSeconds, - retryWithConfiguration: this.ConnectionPolicy.RetryOptions?.GetRetryWithConfiguration(), - enableTcpConnectionEndpointRediscovery: this.ConnectionPolicy.EnableTcpConnectionEndpointRediscovery, - rntbdMaxConcurrentOpeningConnectionCount: this.rntbdMaxConcurrentOpeningConnectionCount, - remoteCertificateValidationCallback: this.remoteCertificateValidationCallback, - distributedTracingOptions: distributedTracingOptions, - enableChannelMultiplexing: ConfigurationManager.IsTcpChannelMultiplexingEnabled(), - chaosInterceptor: this.chaosInterceptor); - - if (this.transportClientHandlerFactory != null) - { - newClientFactory.WithTransportInterceptor(this.transportClientHandlerFactory); - } - - this.storeClientFactory = newClientFactory; - this.isStoreClientFactoryCreatedInternally = true; - } - - this.AddressResolver = new GlobalAddressResolver( - this.GlobalEndpointManager, - this.PartitionKeyRangeLocation, - this.ConnectionPolicy.ConnectionProtocol, - this, - this.collectionCache, - this.partitionKeyRangeCache, - this.accountServiceConfiguration, - this.ConnectionPolicy, - this.httpClient, - this.storeClientFactory.GetConnectionStateListener(), - this.enableAsyncCacheExceptionNoSharing); - - this.CreateStoreModel(subscribeRntbdStatus: true); - } - - private void CreateStoreModel(bool subscribeRntbdStatus) - { - //EnableReadRequestsFallback, if not explicity set on the connection policy, - //is false if the account's consistency is bounded staleness, - //and true otherwise. - StoreClient storeClient = this.storeClientFactory.CreateStoreClient( - this.AddressResolver, - this.sessionContainer, - this.accountServiceConfiguration, - this, - true, - this.ConnectionPolicy.EnableReadRequestsFallback ?? (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.BoundedStaleness), - !this.enableRntbdChannel, - this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), - true, - enableReplicaValidation: this.isReplicaAddressValidationEnabled, - sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); - - if (subscribeRntbdStatus) - { - storeClient.AddDisableRntbdChannelCallback(new Action(this.DisableRntbdChannel)); - } - - storeClient.SerializerSettings = this.serializerSettings; - - this.StoreModel = new ServerStoreModel(storeClient, this.sendingRequest, this.receivedResponse); - } - - private void DisableRntbdChannel() - { - Debug.Assert(this.enableRntbdChannel); - this.enableRntbdChannel = false; - this.CreateStoreModel(subscribeRntbdStatus: false); - } - - private async Task InitializeGatewayConfigurationReaderAsync() - { - GatewayAccountReader accountReader = new GatewayAccountReader( - serviceEndpoint: this.ServiceEndpoint, - cosmosAuthorization: this.cosmosAuthorization, - connectionPolicy: this.ConnectionPolicy, - httpClient: this.httpClient, - cancellationToken: this.cancellationTokenSource.Token, - isThinClientEnabled: this.isThinClientEnabled); - - this.accountServiceConfiguration = new CosmosAccountServiceConfiguration(accountReader.InitializeReaderAsync); - - await this.accountServiceConfiguration.InitializeAsync(); - AccountProperties accountProperties = this.accountServiceConfiguration.AccountProperties; - this.UseMultipleWriteLocations = this.ConnectionPolicy.UseMultipleWriteLocations && accountProperties.EnableMultipleWriteLocations; - this.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(accountProperties); - } - - internal string GetUserAgentFeatures() - { - int featureFlag = 0; - if (this.ConnectionPolicy.EnablePartitionLevelFailover) - { - featureFlag += (int)UserAgentFeatureFlags.PerPartitionAutomaticFailover; - } - - if (this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker) - { - featureFlag += (int)UserAgentFeatureFlags.PerPartitionCircuitBreaker; - } - - if (this.isThinClientEnabled) - { - featureFlag += (int)UserAgentFeatureFlags.ThinClient; - } - - if (ConfigurationManager.IsBinaryEncodingEnabled()) - { - featureFlag += (int)UserAgentFeatureFlags.BinaryEncoding; + featureFlag += (int)UserAgentFeatureFlags.BinaryEncoding; } return featureFlag == 0 ? string.Empty : $"F{featureFlag:X}"; @@ -6893,379 +6892,379 @@ internal void InitializePartitionLevelFailoverWithDefaultHedging() thresholdStep: TimeSpan.FromMilliseconds(DocumentClient.DefaultHedgingThresholdStepInMilliseconds)); } } - - internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) - { - this.sessionContainer.SetSessionToken(request, response.Headers); - } - - internal DocumentServiceRequest CreateDocumentServiceRequest( - OperationType operationType, - string resourceLink, - ResourceType resourceType, - INameValueCollection headers) - { - if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) - { - return DocumentServiceRequest.Create( - operationType, - null, - resourceType, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - else - { - return DocumentServiceRequest.Create( - operationType, - resourceType, - resourceLink, - AuthorizationTokenType.PrimaryMasterKey, - headers); - } - } - - internal void ValidateResource(Documents.Resource resource) - { - this.ValidateResource(resource.Id); - } - - internal void ValidateResource(string resourceId) - { - if (!string.IsNullOrEmpty(resourceId)) - { - int match = resourceId.IndexOfAny(resourceIdSeparators); - if (match != -1) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidCharacterInResourceName, - resourceId[match])); - } - - if (resourceId[resourceId.Length - 1] == ' ') - { - throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); - } - } - } - - private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - PartitionKeyInternal partitionKey; - if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else if (options?.PartitionKey != null) - { - partitionKey = options.PartitionKey.InternalKey; - } - else - { - partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) - { - CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); - ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); - PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; - - // For backward compatibility, if collection doesn't have partition key defined, we assume all documents - // have empty value for it and user doesn't need to specify it explicitly. - PartitionKeyInternal partitionKey; - if (options?.PartitionKey == null) - { - if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) - { - partitionKey = PartitionKeyInternal.Empty; - } - else - { - throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); - } - } - else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) - { - partitionKey = collection.GetNoneValue(); - } - else - { - partitionKey = options.PartitionKey.InternalKey; - } - - request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); - } - - private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) - { - return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; - } - - private INameValueCollection GetRequestHeaders( - Documents.Client.RequestOptions options, - OperationType operationType, - ResourceType resourceType) - { - Debug.Assert( - this.isSuccessfullyInitialized, - "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); - - RequestNameValueCollection headers = new RequestNameValueCollection(); - - if (this.UseMultipleWriteLocations) - { - headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); - } - - if (this.desiredConsistencyLevel.HasValue) - { - // check anyways since default consistency level might have been refreshed. - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: this.desiredConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); - } - - if (options == null) - { - return headers; - } - - if (options.AccessCondition != null) - { - if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) - { - headers.IfMatch = options.AccessCondition.Condition; - } - else - { - headers.IfNoneMatch = options.AccessCondition.Condition; - } - } - - if (options.ConsistencyLevel.HasValue) - { - if (!this.IsValidConsistency( - backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, - desiredConsistency: options.ConsistencyLevel.Value, - operationType: operationType, - resourceType: resourceType)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentUICulture, - RMResources.InvalidConsistencyLevel, - options.ConsistencyLevel.Value.ToString(), - this.accountServiceConfiguration.DefaultConsistencyLevel)); - } - - headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); - } - - if (options.PriorityLevel.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); - } - - if (options.IndexingDirective.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); - } - - if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) - { - string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); - } - - if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) - { - string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); - headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); - } - - if (!string.IsNullOrEmpty(options.SessionToken)) - { - headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; - } - - if (options.ResourceTokenExpirySeconds.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferType != null) - { - headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); - } - - if (options.OfferThroughput.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.OfferEnableRUPerMinuteThroughput) - { - headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); - } - - if (options.InsertSystemPartitionKey) - { - headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); - } - - //if (options.OfferAutopilotTier.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); - //} - - //if (options.OfferAutopilotAutoUpgrade.HasValue) - //{ - // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); - //} - - if (options.EnableScriptLogging) - { - headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); - } - - if (options.PopulateQuotaInfo) - { - headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); - } - - if (options.PopulateRestoreStatus) - { - headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); - } - - if (options.PopulatePartitionKeyRangeStatistics) - { - headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); - } - - if (options.DisableRUPerMinuteUsage) - { - headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); - } - - if (options.RemoteStorageType.HasValue) - { - headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); - } - - if (options.PartitionKeyRangeId != null) - { - headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); - } - - if (options.SourceDatabaseId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); - } - - if (options.SourceCollectionId != null) - { - headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); - } - - if (options.RestorePointInTime.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.IsReadOnlyScript) - { - headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); - } - - if (options.IncludeSnapshotDirectories) - { - headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); - } - - if (options.ExcludeSystemProperties.HasValue) - { - headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); - } - - if (options.MergeStaticId != null) - { - headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); - } - - if (options.PreserveFullContent) - { - headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); - } - - if (options.ThroughputBucket.HasValue) - { - headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); - } - - return headers; - } - - private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory - { - private readonly IRetryPolicyFactory retryPolicy; - private readonly ISessionContainer sessionContainer; - private readonly ClientCollectionCache collectionCache; - - public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) - { - this.retryPolicy = retryPolicy; - this.sessionContainer = sessionContainer; - this.collectionCache = collectionCache; - } - - public IDocumentClientRetryPolicy GetRequestPolicy() - { - return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); - } - } - - private class HttpRequestMessageHandler : DelegatingHandler - { - private readonly EventHandler sendingRequest; - private readonly EventHandler receivedResponse; - - public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) - { - this.sendingRequest = sendingRequest; - this.receivedResponse = receivedResponse; - - this.InnerHandler = innerHandler ?? new HttpClientHandler(); - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); - HttpResponseMessage response = await base.SendAsync(request, cancellationToken); - this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); - return response; - } - } - - #endregion - } -} + + internal void CaptureSessionToken(DocumentServiceRequest request, DocumentServiceResponse response) + { + this.sessionContainer.SetSessionToken(request, response.Headers); + } + + internal DocumentServiceRequest CreateDocumentServiceRequest( + OperationType operationType, + string resourceLink, + ResourceType resourceType, + INameValueCollection headers) + { + if (resourceType == ResourceType.Database || resourceType == ResourceType.Offer) + { + return DocumentServiceRequest.Create( + operationType, + null, + resourceType, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + else + { + return DocumentServiceRequest.Create( + operationType, + resourceType, + resourceLink, + AuthorizationTokenType.PrimaryMasterKey, + headers); + } + } + + internal void ValidateResource(Documents.Resource resource) + { + this.ValidateResource(resource.Id); + } + + internal void ValidateResource(string resourceId) + { + if (!string.IsNullOrEmpty(resourceId)) + { + int match = resourceId.IndexOfAny(resourceIdSeparators); + if (match != -1) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidCharacterInResourceName, + resourceId[match])); + } + + if (resourceId[resourceId.Length - 1] == ' ') + { + throw new ArgumentException(RMResources.InvalidSpaceEndingInResourceName); + } + } + } + + private async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Document document, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + PartitionKeyInternal partitionKey; + if (options?.PartitionKey != null && options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else if (options?.PartitionKey != null) + { + partitionKey = options.PartitionKey.InternalKey; + } + else + { + partitionKey = DocumentAnalyzer.ExtractPartitionKeyValue(document, partitionKeyDefinition); + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + internal async Task AddPartitionKeyInformationAsync(DocumentServiceRequest request, Documents.Client.RequestOptions options) + { + CollectionCache collectionCache = await this.GetCollectionCacheAsync(NoOpTrace.Singleton); + ContainerProperties collection = await collectionCache.ResolveCollectionAsync(request, CancellationToken.None, NoOpTrace.Singleton); + PartitionKeyDefinition partitionKeyDefinition = collection.PartitionKey; + + // For backward compatibility, if collection doesn't have partition key defined, we assume all documents + // have empty value for it and user doesn't need to specify it explicitly. + PartitionKeyInternal partitionKey; + if (options?.PartitionKey == null) + { + if (partitionKeyDefinition == null || partitionKeyDefinition.Paths.Count == 0) + { + partitionKey = PartitionKeyInternal.Empty; + } + else + { + throw new InvalidOperationException(RMResources.MissingPartitionKeyValue); + } + } + else if (options.PartitionKey.Equals(Documents.PartitionKey.None)) + { + partitionKey = collection.GetNoneValue(); + } + else + { + partitionKey = options.PartitionKey.InternalKey; + } + + request.Headers.Set(HttpConstants.HttpHeaders.PartitionKey, partitionKey.ToJsonString()); + } + + private JsonSerializerSettings GetSerializerSettingsForRequest(Documents.Client.RequestOptions requestOptions) + { + return requestOptions?.JsonSerializerSettings ?? this.serializerSettings; + } + + private INameValueCollection GetRequestHeaders( + Documents.Client.RequestOptions options, + OperationType operationType, + ResourceType resourceType) + { + Debug.Assert( + this.isSuccessfullyInitialized, + "GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property"); + + RequestNameValueCollection headers = new RequestNameValueCollection(); + + if (this.UseMultipleWriteLocations) + { + headers.Set(HttpConstants.HttpHeaders.AllowTentativeWrites, bool.TrueString); + } + + if (this.desiredConsistencyLevel.HasValue) + { + // check anyways since default consistency level might have been refreshed. + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: this.desiredConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString(); + } + + if (options == null) + { + return headers; + } + + if (options.AccessCondition != null) + { + if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch) + { + headers.IfMatch = options.AccessCondition.Condition; + } + else + { + headers.IfNoneMatch = options.AccessCondition.Condition; + } + } + + if (options.ConsistencyLevel.HasValue) + { + if (!this.IsValidConsistency( + backendConsistency: this.accountServiceConfiguration.DefaultConsistencyLevel, + desiredConsistency: options.ConsistencyLevel.Value, + operationType: operationType, + resourceType: resourceType)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, + RMResources.InvalidConsistencyLevel, + options.ConsistencyLevel.Value.ToString(), + this.accountServiceConfiguration.DefaultConsistencyLevel)); + } + + headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, options.ConsistencyLevel.ToString()); + } + + if (options.PriorityLevel.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.PriorityLevel, options.PriorityLevel.ToString()); + } + + if (options.IndexingDirective.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.IndexingDirective, options.IndexingDirective.ToString()); + } + + if (options.PostTriggerInclude != null && options.PostTriggerInclude.Count > 0) + { + string postTriggerInclude = string.Join(",", options.PostTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PostTriggerInclude, postTriggerInclude); + } + + if (options.PreTriggerInclude != null && options.PreTriggerInclude.Count > 0) + { + string preTriggerInclude = string.Join(",", options.PreTriggerInclude.AsEnumerable()); + headers.Set(HttpConstants.HttpHeaders.PreTriggerInclude, preTriggerInclude); + } + + if (!string.IsNullOrEmpty(options.SessionToken)) + { + headers[HttpConstants.HttpHeaders.SessionToken] = options.SessionToken; + } + + if (options.ResourceTokenExpirySeconds.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ResourceTokenExpiry, options.ResourceTokenExpirySeconds.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferType != null) + { + headers.Set(HttpConstants.HttpHeaders.OfferType, options.OfferType); + } + + if (options.OfferThroughput.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.OfferThroughput, options.OfferThroughput.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.OfferEnableRUPerMinuteThroughput) + { + headers.Set(HttpConstants.HttpHeaders.OfferIsRUPerMinuteThroughputEnabled, bool.TrueString); + } + + if (options.InsertSystemPartitionKey) + { + headers.Set(HttpConstants.HttpHeaders.InsertSystemPartitionKey, bool.TrueString); + } + + //if (options.OfferAutopilotTier.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotTier, options.OfferAutopilotTier.ToString()); + //} + + //if (options.OfferAutopilotAutoUpgrade.HasValue) + //{ + // headers.Set(HttpConstants.HttpHeaders.OfferAutopilotAutoUpgrade, options.OfferAutopilotAutoUpgrade.ToString()); + //} + + if (options.EnableScriptLogging) + { + headers.Set(HttpConstants.HttpHeaders.EnableLogging, bool.TrueString); + } + + if (options.PopulateQuotaInfo) + { + headers.Set(HttpConstants.HttpHeaders.PopulateQuotaInfo, bool.TrueString); + } + + if (options.PopulateRestoreStatus) + { + headers.Set(HttpConstants.HttpHeaders.PopulateRestoreStatus, bool.TrueString); + } + + if (options.PopulatePartitionKeyRangeStatistics) + { + headers.Set(HttpConstants.HttpHeaders.PopulatePartitionStatistics, bool.TrueString); + } + + if (options.DisableRUPerMinuteUsage) + { + headers.Set(HttpConstants.HttpHeaders.DisableRUPerMinuteUsage, bool.TrueString); + } + + if (options.RemoteStorageType.HasValue) + { + headers.Set(WFConstants.BackendHeaders.RemoteStorageType, options.RemoteStorageType.ToString()); + } + + if (options.PartitionKeyRangeId != null) + { + headers.Set(WFConstants.BackendHeaders.PartitionKeyRangeId, options.PartitionKeyRangeId); + } + + if (options.SourceDatabaseId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceDatabaseId, options.SourceDatabaseId); + } + + if (options.SourceCollectionId != null) + { + headers.Set(HttpConstants.HttpHeaders.SourceCollectionId, options.SourceCollectionId); + } + + if (options.RestorePointInTime.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.RestorePointInTime, options.RestorePointInTime.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.IsReadOnlyScript) + { + headers.Set(HttpConstants.HttpHeaders.IsReadOnlyScript, bool.TrueString); + } + + if (options.IncludeSnapshotDirectories) + { + headers.Set(HttpConstants.HttpHeaders.IncludeSnapshotDirectories, bool.TrueString); + } + + if (options.ExcludeSystemProperties.HasValue) + { + headers.Set(WFConstants.BackendHeaders.ExcludeSystemProperties, options.ExcludeSystemProperties.Value.ToString()); + } + + if (options.MergeStaticId != null) + { + headers.Set(HttpConstants.HttpHeaders.MergeStaticId, options.MergeStaticId); + } + + if (options.PreserveFullContent) + { + headers.Set(HttpConstants.HttpHeaders.PreserveFullContent, bool.TrueString); + } + + if (options.ThroughputBucket.HasValue) + { + headers.Set(HttpConstants.HttpHeaders.ThroughputBucket, options.ThroughputBucket?.ToString(CultureInfo.InvariantCulture)); + } + + return headers; + } + + private class ResetSessionTokenRetryPolicyFactory : IRetryPolicyFactory + { + private readonly IRetryPolicyFactory retryPolicy; + private readonly ISessionContainer sessionContainer; + private readonly ClientCollectionCache collectionCache; + + public ResetSessionTokenRetryPolicyFactory(ISessionContainer sessionContainer, ClientCollectionCache collectionCache, IRetryPolicyFactory retryPolicy) + { + this.retryPolicy = retryPolicy; + this.sessionContainer = sessionContainer; + this.collectionCache = collectionCache; + } + + public IDocumentClientRetryPolicy GetRequestPolicy() + { + return new RenameCollectionAwareClientRetryPolicy(this.sessionContainer, this.collectionCache, this.retryPolicy.GetRequestPolicy()); + } + } + + private class HttpRequestMessageHandler : DelegatingHandler + { + private readonly EventHandler sendingRequest; + private readonly EventHandler receivedResponse; + + public HttpRequestMessageHandler(EventHandler sendingRequest, EventHandler receivedResponse, HttpMessageHandler innerHandler) + { + this.sendingRequest = sendingRequest; + this.receivedResponse = receivedResponse; + + this.InnerHandler = innerHandler ?? new HttpClientHandler(); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.sendingRequest?.Invoke(this, new SendingRequestEventArgs(request)); + HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + this.receivedResponse?.Invoke(this, new ReceivedResponseEventArgs(request, response)); + return response; + } + } + + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index 067cb0fc15..aab6f1d86a 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -1,66 +1,69 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos -{ - using System; +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; using System.Collections.Concurrent; using System.Diagnostics; - using System.IO; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Routing; + using System.IO; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.FaultInjection; - using Newtonsoft.Json; - using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; - - /// - /// A TransportClient that sends requests to proxy endpoint. - /// And then processes the response back into DocumentServiceResponse objects. - /// - internal class ThinClientStoreClient : GatewayStoreClient - { - private readonly bool isPartitionLevelFailoverEnabled; - private readonly ObjectPool bufferProviderWrapperPool; + using Microsoft.Azure.Documents.FaultInjection; + using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.ThinClientTransportSerializer; + + /// + /// A TransportClient that sends requests to proxy endpoint. + /// And then processes the response back into DocumentServiceResponse objects. + /// + internal class ThinClientStoreClient : GatewayStoreClient + { + private readonly bool isPartitionLevelFailoverEnabled; + private readonly ObjectPool bufferProviderWrapperPool; + private readonly UserAgentContainer userAgentContainer; private readonly IChaosInterceptor chaosInterceptor; - - public ThinClientStoreClient( + + public ThinClientStoreClient( CosmosHttpClient httpClient, + UserAgentContainer userAgentContainer, ICommunicationEventSource eventSource, + bool isPartitionLevelFailoverEnabled = false, JsonSerializerSettings serializerSettings = null, - bool isPartitionLevelFailoverEnabled = false, - IChaosInterceptor chaosInterceptor = null) - : base(httpClient, - eventSource, + IChaosInterceptor chaosInterceptor = null) + : base(httpClient, + eventSource, serializerSettings, - isPartitionLevelFailoverEnabled) - { - this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; - this.chaosInterceptor = chaosInterceptor; - } - - public override async Task InvokeAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceType, - physicalAddress, - thinClientEndpoint, - globalDatabaseAccountName, - clientCollectionCache, - cancellationToken)) + isPartitionLevelFailoverEnabled) + { + this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); + this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; + this.userAgentContainer = userAgentContainer; + this.chaosInterceptor = chaosInterceptor; + } + + public override async Task InvokeAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceType, + physicalAddress, + thinClientEndpoint, + globalDatabaseAccountName, + clientCollectionCache, + cancellationToken)) { if (this.chaosInterceptor != null) { @@ -74,42 +77,43 @@ public override async Task InvokeAsync( return await ThinClientStoreClient.ParseResponseAsync(fiResponseMessage, request.SerializerSettings ?? base.SerializerSettings, request); } } - HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); - return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); - } - } - - internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) - { - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? - HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : - HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); - - using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( - request, - resourceOperation.resourceType, - physicalAddress, - default, - default, - default, - default)) - { - return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); - } - } - - private async ValueTask PrepareRequestForProxyAsync( - DocumentServiceRequest request, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache) - { - HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; - requestMessage.Version = new Version(2, 0); - - BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); - try + + HttpResponseMessage proxyResponse = await ThinClientTransportSerializer.ConvertProxyResponseAsync(responseMessage); + return await ThinClientStoreClient.ParseResponseAsync(proxyResponse, request.SerializerSettings ?? base.SerializerSettings, request); + } + } + + internal override async Task InvokeStoreAsync(Uri baseAddress, ResourceOperation resourceOperation, DocumentServiceRequest request) + { + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) ? + HttpTransportClient.GetResourceFeedUri(resourceOperation.resourceType, baseAddress, request) : + HttpTransportClient.GetResourceEntryUri(resourceOperation.resourceType, baseAddress, request); + + using (HttpResponseMessage responseMessage = await this.InvokeClientAsync( + request, + resourceOperation.resourceType, + physicalAddress, + default, + default, + default, + default)) + { + return await HttpTransportClient.ProcessHttpResponse(request.ResourceAddress, string.Empty, responseMessage, physicalAddress, request); + } + } + + private async ValueTask PrepareRequestForProxyAsync( + DocumentServiceRequest request, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache) + { + HttpRequestMessage requestMessage = base.PrepareRequestMessageAsync(request, physicalAddress).Result; + requestMessage.Version = new Version(2, 0); + + BufferProviderWrapper bufferProviderWrapper = this.bufferProviderWrapperPool.Get(); + try { PartitionKeyRange partitionKeyRange = request.RequestContext?.ResolvedPartitionKeyRange; @@ -123,90 +127,90 @@ private async ValueTask PrepareRequestForProxyAsync( ThinClientConstants.ProxyEndEpk, partitionKeyRange?.MaxExclusive); } - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyOperationType, - request.OperationType.ToOperationTypeString()); - - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.ProxyResourceType, - request.ResourceType.ToResourceTypeString()); - - Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( - bufferProviderWrapper, - globalDatabaseAccountName, - clientCollectionCache, - requestMessage); - - if (!contentStream.CanSeek) - { - throw new InvalidOperationException( - $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); - } - - requestMessage.Content = new StreamContent(contentStream); - requestMessage.Content.Headers.ContentLength = contentStream.Length; + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyOperationType, + request.OperationType.ToOperationTypeString()); + + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.ProxyResourceType, + request.ResourceType.ToResourceTypeString()); + + Stream contentStream = await ThinClientTransportSerializer.SerializeProxyRequestAsync( + bufferProviderWrapper, + globalDatabaseAccountName, + clientCollectionCache, + requestMessage); + + if (!contentStream.CanSeek) + { + throw new InvalidOperationException( + $"The serializer returned a non-seekable stream ({contentStream.GetType().FullName})."); + } + + requestMessage.Content = new StreamContent(contentStream); + requestMessage.Content.Headers.ContentLength = contentStream.Length; requestMessage.Headers.Clear(); - requestMessage.Headers.TryAddWithoutValidation( - ThinClientConstants.UserAgent, + requestMessage.Headers.TryAddWithoutValidation( + ThinClientConstants.UserAgent, this.userAgentContainer.UserAgent); - + Guid activityId = Trace.CorrelationManager.ActivityId; - Debug.Assert(activityId != Guid.Empty); - requestMessage.Headers.TryAddWithoutValidation( + Debug.Assert(activityId != Guid.Empty); + requestMessage.Headers.TryAddWithoutValidation( HttpConstants.HttpHeaders.ActivityId, activityId.ToString()); - requestMessage.RequestUri = thinClientEndpoint; - requestMessage.Method = HttpMethod.Post; - - return requestMessage; - } - finally - { - this.bufferProviderWrapperPool.Return(bufferProviderWrapper); - } - } - - private Task InvokeClientAsync( - DocumentServiceRequest request, - ResourceType resourceType, - Uri physicalAddress, - Uri thinClientEndpoint, - string globalDatabaseAccountName, - ClientCollectionCache clientCollectionCache, - CancellationToken cancellationToken) - { - DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); - return base.httpClient.SendHttpAsync( - () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), - resourceType, - HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), - request.RequestContext.ClientRequestStatistics, + requestMessage.RequestUri = thinClientEndpoint; + requestMessage.Method = HttpMethod.Post; + + return requestMessage; + } + finally + { + this.bufferProviderWrapperPool.Return(bufferProviderWrapper); + } + } + + private Task InvokeClientAsync( + DocumentServiceRequest request, + ResourceType resourceType, + Uri physicalAddress, + Uri thinClientEndpoint, + string globalDatabaseAccountName, + ClientCollectionCache clientCollectionCache, + CancellationToken cancellationToken) + { + DefaultTrace.TraceInformation("In {0}, OperationType: {1}, ResourceType: {2}", nameof(ThinClientStoreClient), request.OperationType, request.ResourceType); + return base.httpClient.SendHttpAsync( + () => this.PrepareRequestForProxyAsync(request, physicalAddress, thinClientEndpoint, globalDatabaseAccountName, clientCollectionCache), + resourceType, + HttpTimeoutPolicy.GetTimeoutPolicy(request, isThinClientEnabled: true), + request.RequestContext.ClientRequestStatistics, cancellationToken, - request); - } - - internal class ObjectPool - { - private readonly ConcurrentBag Objects; - private readonly Func ObjectGenerator; - - public ObjectPool(Func objectGenerator) - { - this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); - this.Objects = new ConcurrentBag(); - } - - public T Get() - { - return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); - } - - public void Return(T item) - { - this.Objects.Add(item); - } - } - } + request); + } + + internal class ObjectPool + { + private readonly ConcurrentBag Objects; + private readonly Func ObjectGenerator; + + public ObjectPool(Func objectGenerator) + { + this.ObjectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); + this.Objects = new ConcurrentBag(); + } + + public T Get() + { + return this.Objects.TryTake(out T item) ? item : this.ObjectGenerator(); + } + + public void Return(T item) + { + this.Objects.Add(item); + } + } + } } \ No newline at end of file From 4f9253a1ea9567a269003afe1befd8aee7db86d0 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:32:54 -0700 Subject: [PATCH 14/20] Update DocumentClient.cs --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index 7b315b4116..7f542fd938 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -1090,7 +1090,8 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli this.serializerSettings, this.httpClient, this.PartitionKeyRangeLocation, - isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker); + isPartitionLevelFailoverEnabled: this.ConnectionPolicy.EnablePartitionLevelFailover || this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker, + this.chaosInterceptor); this.GatewayStoreModel = gatewayStoreModel; From dc1cd44c5355a1b6b863f88df56dd9a05ef4e52f Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:05:03 -0400 Subject: [PATCH 15/20] Update GatewayStoreModel.cs --- Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 5b902afed1..2bb15d52aa 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -68,6 +68,7 @@ public GatewayStoreModel( this.eventSource, serializerSettings, isPartitionLevelFailoverEnabled); + this.chaosInterceptor = chaosInterceptor; if (isThinClientEnabled) { @@ -76,12 +77,12 @@ public GatewayStoreModel( userAgentContainer, this.eventSource, isPartitionLevelFailoverEnabled, - serializerSettings); + serializerSettings, + this.chaosInterceptor); } this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( this.MarkEndpointsToHealthyAsync); - this.chaosInterceptor = chaosInterceptor; } public virtual async Task ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default) From 353c7433fd6015414bdab02759b6cda4e8b619cd Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Tue, 12 Aug 2025 15:31:03 -0400 Subject: [PATCH 16/20] Update GatewayStoreModel.cs --- Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 2bb15d52aa..d293f26ba6 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -63,12 +63,12 @@ public GatewayStoreModel( this.defaultConsistencyLevel = defaultConsistencyLevel; this.eventSource = eventSource; this.globalPartitionEndpointManager = globalPartitionEndpointManager; + this.chaosInterceptor = chaosInterceptor; this.gatewayStoreClient = new GatewayStoreClient( httpClient, this.eventSource, serializerSettings, isPartitionLevelFailoverEnabled); - this.chaosInterceptor = chaosInterceptor; if (isThinClientEnabled) { From c0a6084981d5c01ceca98c775ba14965f4dd3950 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:29:49 -0400 Subject: [PATCH 17/20] fixes merge --- Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs | 2 +- Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index a40859b32d..7d0f5672cb 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -39,7 +39,6 @@ internal class GatewayStoreModel : IStoreModelExtension, IDisposable // Caches to resolve the PartitionKeyRange from request. For Session Token Optimization. protected PartitionKeyRangeCache partitionKeyRangeCache; protected ClientCollectionCache clientCollectionCache; - protected ISessionContainer sessionContainer; public GatewayStoreModel( GlobalEndpointManager endpointManager, @@ -62,6 +61,7 @@ public GatewayStoreModel( this.gatewayStoreClient = new GatewayStoreClient( httpClient, this.eventSource, + globalPartitionEndpointManager, serializerSettings); if (isThinClientEnabled) diff --git a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs index c7ed1f1642..8279210ac8 100644 --- a/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs +++ b/Microsoft.Azure.Cosmos/src/ThinClientStoreClient.cs @@ -29,21 +29,20 @@ internal class ThinClientStoreClient : GatewayStoreClient private readonly UserAgentContainer userAgentContainer; private readonly IChaosInterceptor chaosInterceptor; - public ThinClientStoreClient( CosmosHttpClient httpClient, UserAgentContainer userAgentContainer, ICommunicationEventSource eventSource, - bool isPartitionLevelFailoverEnabled = false, + GlobalPartitionEndpointManager globalPartitionEndpointManager, JsonSerializerSettings serializerSettings = null, IChaosInterceptor chaosInterceptor = null) : base(httpClient, eventSource, - serializerSettings, - isPartitionLevelFailoverEnabled) + globalPartitionEndpointManager, + serializerSettings) { this.bufferProviderWrapperPool = new ObjectPool(() => new BufferProviderWrapper()); - this.isPartitionLevelFailoverEnabled = isPartitionLevelFailoverEnabled; + this.globalPartitionEndpointManager = globalPartitionEndpointManager; this.userAgentContainer = userAgentContainer ?? throw new ArgumentNullException(nameof(userAgentContainer), "UserAgentContainer cannot be null when initializing ThinClientStoreClient."); From 4d7be33a688fe9deea0de893b0708344a741b6c4 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:52:42 -0400 Subject: [PATCH 18/20] Copilot suggestions Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../implementation/FaultInjectionServerErrorResultInternal.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs index aa043403f8..e16cdb5489 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs @@ -508,7 +508,7 @@ public HttpResponseMessage GetInjectedServerError(DocumentServiceRequest dsr, st isProxyCall ? FaultInjectionResponseEncoding.GetBytes( GetProxyResponseMessageString((int)StatusCodes.Forbidden, (int)SubStatusCodes.DatabaseAccountNotFound, "DatabaseAccountNotFound", ruleId)) - : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), + : FaultInjectionResponseEncoding.GetBytes($"Fault Injection Server Error: DatabaseAccountNotFound, rule: {ruleId}"))), }; this.SetHttpHeaders(httpResponse, headers, isProxyCall); From 01929f6ba598eb7bb7df511134a3ac22efa31e29 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:52:55 -0400 Subject: [PATCH 19/20] Copilot suggestions Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../implementation/FaultInjectionServerErrorResultInternal.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs index e16cdb5489..d4c2cc6f9d 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs @@ -618,7 +618,6 @@ public static byte[] GetBytes(string value) public static byte[] GetBytesFromHexString(string hexString) { return Convert.FromHexString(hexString); - } } } } From 18020ad81057e10cc282a37c9137d0d14f24a381 Mon Sep 17 00:00:00 2001 From: Debdatta Kunda Date: Wed, 13 Aug 2025 10:03:51 -0700 Subject: [PATCH 20/20] Code changes to clean up Gateway Store Model. Fixing Build Break. --- ...FaultInjectionServerErrorResultInternal.cs | 1 + .../src/GatewayStoreModel.cs | 361 +++++++++--------- 2 files changed, 182 insertions(+), 180 deletions(-) diff --git a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs index d4c2cc6f9d..e16cdb5489 100644 --- a/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs +++ b/Microsoft.Azure.Cosmos/FaultInjection/src/implementation/FaultInjectionServerErrorResultInternal.cs @@ -618,6 +618,7 @@ public static byte[] GetBytes(string value) public static byte[] GetBytesFromHexString(string hexString) { return Convert.FromHexString(hexString); + } } } } diff --git a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs index 7d0f5672cb..5a20581cf1 100644 --- a/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs +++ b/Microsoft.Azure.Cosmos/src/GatewayStoreModel.cs @@ -17,7 +17,7 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; - using Microsoft.Azure.Documents.Collections; + using Microsoft.Azure.Documents.Collections; using Microsoft.Azure.Documents.FaultInjection; using Newtonsoft.Json; @@ -25,141 +25,142 @@ namespace Microsoft.Azure.Cosmos internal class GatewayStoreModel : IStoreModelExtension, IDisposable { private static readonly string sessionConsistencyAsString = ConsistencyLevel.Session.ToString(); - private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; - private readonly ISessionContainer sessionContainer; - private readonly DocumentClientEventSource eventSource; + private readonly GlobalPartitionEndpointManager globalPartitionEndpointManager; + private readonly ISessionContainer sessionContainer; + private readonly DocumentClientEventSource eventSource; private readonly IChaosInterceptor chaosInterceptor; internal readonly GlobalEndpointManager endpointManager; - internal readonly ConsistencyLevel defaultConsistencyLevel; - - private ThinClientStoreClient thinClientStoreClient; + internal readonly ConsistencyLevel defaultConsistencyLevel; + + // Store Clients to send requests to the gateway and/ or thin client endpoints. + private ThinClientStoreClient thinClientStoreClient; private GatewayStoreClient gatewayStoreClient; // Caches to resolve the PartitionKeyRange from request. For Session Token Optimization. - protected PartitionKeyRangeCache partitionKeyRangeCache; - protected ClientCollectionCache clientCollectionCache; - - public GatewayStoreModel( - GlobalEndpointManager endpointManager, - ISessionContainer sessionContainer, - ConsistencyLevel defaultConsistencyLevel, - DocumentClientEventSource eventSource, - JsonSerializerSettings serializerSettings, - CosmosHttpClient httpClient, - GlobalPartitionEndpointManager globalPartitionEndpointManager, - bool isThinClientEnabled, - UserAgentContainer userAgentContainer = null, - IChaosInterceptor chaosInterceptor = null) + private PartitionKeyRangeCache partitionKeyRangeCache; + private ClientCollectionCache clientCollectionCache; + + public GatewayStoreModel( + GlobalEndpointManager endpointManager, + ISessionContainer sessionContainer, + ConsistencyLevel defaultConsistencyLevel, + DocumentClientEventSource eventSource, + JsonSerializerSettings serializerSettings, + CosmosHttpClient httpClient, + GlobalPartitionEndpointManager globalPartitionEndpointManager, + bool isThinClientEnabled, + UserAgentContainer userAgentContainer = null, + IChaosInterceptor chaosInterceptor = null) { this.endpointManager = endpointManager; this.sessionContainer = sessionContainer; this.defaultConsistencyLevel = defaultConsistencyLevel; this.eventSource = eventSource; - this.globalPartitionEndpointManager = globalPartitionEndpointManager; + this.globalPartitionEndpointManager = globalPartitionEndpointManager; this.chaosInterceptor = chaosInterceptor; - this.gatewayStoreClient = new GatewayStoreClient( - httpClient, - this.eventSource, - globalPartitionEndpointManager, + this.gatewayStoreClient = new GatewayStoreClient( + httpClient, + this.eventSource, + globalPartitionEndpointManager, serializerSettings); - - if (isThinClientEnabled) - { - this.thinClientStoreClient = new ThinClientStoreClient( - httpClient, - userAgentContainer, - this.eventSource, - globalPartitionEndpointManager, - serializerSettings, - this.chaosInterceptor); - } - - this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( + + if (isThinClientEnabled) + { + this.thinClientStoreClient = new ThinClientStoreClient( + httpClient, + userAgentContainer, + this.eventSource, + globalPartitionEndpointManager, + serializerSettings, + this.chaosInterceptor); + } + + this.globalPartitionEndpointManager.SetBackgroundConnectionPeriodicRefreshTask( this.MarkEndpointsToHealthyAsync); } public virtual async Task ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken = default) - { - DocumentServiceResponse response; - - await GatewayStoreModel.ApplySessionTokenAsync( - request, - this.defaultConsistencyLevel, - this.sessionContainer, - this.partitionKeyRangeCache, - this.clientCollectionCache, - this.endpointManager); - try - { - if (request.ResourceType.Equals(ResourceType.Document) && - this.endpointManager.TryGetLocationForGatewayDiagnostics(request.RequestContext.LocationEndpointToRoute, out string regionName)) - { - request.RequestContext.RegionName = regionName; - } - - // This is applicable for both per partition automatic failover and per partition circuit breaker. - if (this.IsPartitionLevelFailoverEnabled() - && !ReplicatedResourceClient.IsMasterResource(request.ResourceType) - && request.ResourceType.IsPartitioned()) - { - (bool isSuccess, PartitionKeyRange partitionKeyRange) = await TryResolvePartitionKeyRangeAsync( - request: request, - sessionContainer: this.sessionContainer, - partitionKeyRangeCache: this.partitionKeyRangeCache, - clientCollectionCache: this.clientCollectionCache, - refreshCache: false); - - request.RequestContext.ResolvedPartitionKeyRange = partitionKeyRange; - this.globalPartitionEndpointManager.TryAddPartitionLevelLocationOverride(request); - } - - bool canUseThinClient = - this.thinClientStoreClient != null && - GatewayStoreModel.IsOperationSupportedByThinClient(request); - - Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) - ? this.GetFeedUri(request) - : this.GetEntityUri(request); - - if (canUseThinClient) - { - Uri thinClientEndpoint = this.endpointManager.ResolveThinClientEndpoint(request); - - AccountProperties account = await this.GetDatabaseAccountPropertiesAsync(); - - response = await this.thinClientStoreClient.InvokeAsync( - request, - request.ResourceType, - physicalAddress, - thinClientEndpoint, - account.Id, - this.clientCollectionCache, - cancellationToken); - } - else - { - response = await this.gatewayStoreClient.InvokeAsync( - request, - request.ResourceType, - physicalAddress, - cancellationToken); - } - } - catch (DocumentClientException exception) - { - if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) && - (exception.StatusCode == HttpStatusCode.PreconditionFailed || exception.StatusCode == HttpStatusCode.Conflict - || (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable))) + { + DocumentServiceResponse response; + + await GatewayStoreModel.ApplySessionTokenAsync( + request, + this.defaultConsistencyLevel, + this.sessionContainer, + this.partitionKeyRangeCache, + this.clientCollectionCache, + this.endpointManager); + try + { + if (request.ResourceType.Equals(ResourceType.Document) && + this.endpointManager.TryGetLocationForGatewayDiagnostics(request.RequestContext.LocationEndpointToRoute, out string regionName)) + { + request.RequestContext.RegionName = regionName; + } + + // This is applicable for both per partition automatic failover and per partition circuit breaker. + if (this.IsPartitionLevelFailoverEnabled() + && !ReplicatedResourceClient.IsMasterResource(request.ResourceType) + && request.ResourceType.IsPartitioned()) + { + (bool isSuccess, PartitionKeyRange partitionKeyRange) = await TryResolvePartitionKeyRangeAsync( + request: request, + sessionContainer: this.sessionContainer, + partitionKeyRangeCache: this.partitionKeyRangeCache, + clientCollectionCache: this.clientCollectionCache, + refreshCache: false); + + request.RequestContext.ResolvedPartitionKeyRange = partitionKeyRange; + this.globalPartitionEndpointManager.TryAddPartitionLevelLocationOverride(request); + } + + bool canUseThinClient = + this.thinClientStoreClient != null && + GatewayStoreModel.IsOperationSupportedByThinClient(request); + + Uri physicalAddress = ThinClientStoreClient.IsFeedRequest(request.OperationType) + ? this.GetFeedUri(request) + : this.GetEntityUri(request); + + if (canUseThinClient) + { + Uri thinClientEndpoint = this.endpointManager.ResolveThinClientEndpoint(request); + + AccountProperties account = await this.GetDatabaseAccountPropertiesAsync(); + + response = await this.thinClientStoreClient.InvokeAsync( + request, + request.ResourceType, + physicalAddress, + thinClientEndpoint, + account.Id, + this.clientCollectionCache, + cancellationToken); + } + else + { + response = await this.gatewayStoreClient.InvokeAsync( + request, + request.ResourceType, + physicalAddress, + cancellationToken); + } + } + catch (DocumentClientException exception) + { + if ((!ReplicatedResourceClient.IsMasterResource(request.ResourceType)) && + (exception.StatusCode == HttpStatusCode.PreconditionFailed || exception.StatusCode == HttpStatusCode.Conflict + || (exception.StatusCode == HttpStatusCode.NotFound && exception.GetSubStatus() != SubStatusCodes.ReadSessionNotAvailable))) { await this.CaptureSessionTokenAndHandleSplitAsync(exception.StatusCode, exception.GetSubStatus(), request, exception.Headers); - } - - throw; - } - + } + + throw; + } + await this.CaptureSessionTokenAndHandleSplitAsync(response.StatusCode, response.SubStatusCode, request, response.Headers); - return response; + return response; } public virtual async Task GetDatabaseAccountAsync(Func> requestMessage, @@ -410,12 +411,12 @@ internal static async Task> TryResolveSessionTokenAsync( return new Tuple(false, null); } - - private bool IsPartitionLevelFailoverEnabled() - { - return this.globalPartitionEndpointManager.IsPartitionLevelCircuitBreakerEnabled() - || this.globalPartitionEndpointManager.IsPartitionLevelAutomaticFailoverEnabled(); - } + + private bool IsPartitionLevelFailoverEnabled() + { + return this.globalPartitionEndpointManager.IsPartitionLevelCircuitBreakerEnabled() + || this.globalPartitionEndpointManager.IsPartitionLevelAutomaticFailoverEnabled(); + } private static async Task> TryResolvePartitionKeyRangeAsync( DocumentServiceRequest request, @@ -541,64 +542,64 @@ internal static bool IsStoredProcedureCrudOperation( { return resourceType == ResourceType.StoredProcedure && operationType != Documents.OperationType.ExecuteJavaScript; - } - - internal static bool IsOperationSupportedByThinClient(DocumentServiceRequest request) - { - return request.ResourceType == ResourceType.Document - && (request.OperationType == OperationType.Batch - || request.OperationType == OperationType.Patch - || request.OperationType == OperationType.Create - || request.OperationType == OperationType.Read - || request.OperationType == OperationType.Upsert - || request.OperationType == OperationType.Replace - || request.OperationType == OperationType.Delete - || request.OperationType == OperationType.Query); - } - private async Task GetDatabaseAccountPropertiesAsync() - { - AccountProperties accountProperties = await this.endpointManager.GetDatabaseAccountAsync(); - if (accountProperties != null) - { - return accountProperties; - } - - throw new InvalidOperationException("Failed to retrieve AccountProperties. The response was null."); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (this.thinClientStoreClient != null) - { - try - { - this.thinClientStoreClient.Dispose(); - } - catch (Exception exception) - { - DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", - exception.Message); - } - - this.thinClientStoreClient = null; - } - if (this.gatewayStoreClient != null) - { - try - { - this.gatewayStoreClient.Dispose(); - } - catch (Exception exception) - { - DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", - exception.Message); - } - - this.gatewayStoreClient = null; - } - } + } + + internal static bool IsOperationSupportedByThinClient(DocumentServiceRequest request) + { + return request.ResourceType == ResourceType.Document + && (request.OperationType == OperationType.Batch + || request.OperationType == OperationType.Patch + || request.OperationType == OperationType.Create + || request.OperationType == OperationType.Read + || request.OperationType == OperationType.Upsert + || request.OperationType == OperationType.Replace + || request.OperationType == OperationType.Delete + || request.OperationType == OperationType.Query); + } + private async Task GetDatabaseAccountPropertiesAsync() + { + AccountProperties accountProperties = await this.endpointManager.GetDatabaseAccountAsync(); + if (accountProperties != null) + { + return accountProperties; + } + + throw new InvalidOperationException("Failed to retrieve AccountProperties. The response was null."); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (this.thinClientStoreClient != null) + { + try + { + this.thinClientStoreClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.thinClientStoreClient = null; + } + if (this.gatewayStoreClient != null) + { + try + { + this.gatewayStoreClient.Dispose(); + } + catch (Exception exception) + { + DefaultTrace.TraceWarning("Exception {0} thrown during dispose of HttpClient, this could happen if there are inflight request during the dispose of client", + exception.Message); + } + + this.gatewayStoreClient = null; + } + } } internal Uri GetEntityUri(DocumentServiceRequest entity)