diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/BackendMetricsExtractor.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/BackendMetricsExtractor.cs index 7b3bb38ab4..8e745ebb7f 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/BackendMetricsExtractor.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/BackendMetricsExtractor.cs @@ -75,6 +75,11 @@ public override (ParseFailureReason, BackendMetrics) Visit(QueryPageDiagnostics return (ParseFailureReason.None, backendMetrics); } + public override (ParseFailureReason, BackendMetrics) Visit(CosmosSystemInfo cpuLoadHistory) + { + return BackendMetricsExtractor.MetricsNotFound; + } + public override (ParseFailureReason, BackendMetrics) Visit(AddressResolutionStatistics addressResolutionStatistics) { return BackendMetricsExtractor.MetricsNotFound; diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContext.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContext.cs index 9de3eb2738..51211ea3d6 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContext.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContext.cs @@ -7,6 +7,7 @@ namespace Microsoft.Azure.Cosmos using System.Collections; using System.Collections.Generic; using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Documents.Rntbd; /// /// This represents the diagnostics interface used in the SDK. @@ -33,6 +34,8 @@ internal abstract class CosmosDiagnosticsContext : CosmosDiagnosticsInternal, IE internal abstract bool IsComplete(); + internal abstract void AddDiagnosticsInternal(CosmosSystemInfo cpuLoadHistory); + internal abstract void AddDiagnosticsInternal(PointOperationStatistics pointOperationStatistics); internal abstract void AddDiagnosticsInternal(QueryPageDiagnostics queryPageDiagnostics); diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContextCore.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContextCore.cs index 50c344147a..d7aecc3120 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContextCore.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsContextCore.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos using System.Diagnostics; using System.Linq; using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Documents.Rntbd; /// /// This represents the core diagnostics object used in the SDK. @@ -84,6 +85,16 @@ internal override IDisposable CreateRequestHandlerScopeScope(RequestHandler requ return requestHandlerScope; } + internal override void AddDiagnosticsInternal(CosmosSystemInfo processInfo) + { + if (processInfo == null) + { + throw new ArgumentNullException(nameof(processInfo)); + } + + this.ContextList.Add(processInfo); + } + internal override void AddDiagnosticsInternal(PointOperationStatistics pointOperationStatistics) { if (pointOperationStatistics == null) diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor.cs index 5a8ac71c80..c015860c6f 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor.cs @@ -15,5 +15,6 @@ internal abstract class CosmosDiagnosticsInternalVisitor public abstract void Visit(StoreResponseStatistics storeResponseStatistics); public abstract void Visit(CosmosClientSideRequestStatistics clientSideRequestStatistics); public abstract void Visit(FeedRangeStatistics feedRangeStatistics); + public abstract void Visit(CosmosSystemInfo processInfo); } } diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor{TResult}.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor{TResult}.cs index 7567a32dd8..3c89b2424f 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor{TResult}.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsInternalVisitor{TResult}.cs @@ -15,5 +15,6 @@ internal abstract class CosmosDiagnosticsInternalVisitor public abstract TResult Visit(StoreResponseStatistics storeResponseStatistics); public abstract TResult Visit(CosmosClientSideRequestStatistics clientSideRequestStatistics); public abstract TResult Visit(FeedRangeStatistics feedRangeStatistics); + public abstract TResult Visit(CosmosSystemInfo processInfo); } } diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs index ca684c3991..b21e1eef3c 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos.Diagnostics using System.Globalization; using System.IO; using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Rntbd; using Newtonsoft.Json; internal sealed class CosmosDiagnosticsSerializerVisitor : CosmosDiagnosticsInternalVisitor @@ -281,6 +282,20 @@ public override void Visit(RequestHandlerScope requestHandlerScope) this.jsonWriter.WriteEndObject(); } + public override void Visit(CosmosSystemInfo processInfo) + { + this.jsonWriter.WriteStartObject(); + + this.jsonWriter.WritePropertyName("Id"); + this.jsonWriter.WriteValue("SystemInfo"); + + this.jsonWriter.WritePropertyName("CpuHistory"); + CpuLoadHistory cpuLoadHistory = processInfo.CpuLoadHistory; + this.jsonWriter.WriteValue(cpuLoadHistory.ToString()); + + this.jsonWriter.WriteEndObject(); + } + private void WriteJsonUriArray(string propertyName, IEnumerable uris) { this.jsonWriter.WritePropertyName(propertyName); diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosSystemInfo.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosSystemInfo.cs new file mode 100644 index 0000000000..2c9b3c73a5 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosSystemInfo.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Diagnostics +{ + using System; + using Microsoft.Azure.Documents.Rntbd; + + internal sealed class CosmosSystemInfo : CosmosDiagnosticsInternal + { + public readonly CpuLoadHistory CpuLoadHistory; + + public CosmosSystemInfo( + CpuLoadHistory cpuLoadHistory) + { + this.CpuLoadHistory = cpuLoadHistory ?? throw new ArgumentNullException(nameof(cpuLoadHistory)); + } + + public override void Accept(CosmosDiagnosticsInternalVisitor visitor) + { + visitor.Visit(this); + } + + public override TResult Accept(CosmosDiagnosticsInternalVisitor visitor) + { + return visitor.Visit(this); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/EmptyCosmosDiagnosticsContext.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/EmptyCosmosDiagnosticsContext.cs index de7d7f1838..6297678654 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/EmptyCosmosDiagnosticsContext.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/EmptyCosmosDiagnosticsContext.cs @@ -50,6 +50,10 @@ internal override IDisposable CreateRequestHandlerScopeScope(RequestHandler requ return EmptyCosmosDiagnosticsContext.DefaultScope; } + internal override void AddDiagnosticsInternal(CosmosSystemInfo cpuLoadHistory) + { + } + internal override void AddDiagnosticsInternal(PointOperationStatistics pointOperationStatistics) { } diff --git a/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs b/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs index 84292b17c0..090da36430 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs @@ -14,6 +14,7 @@ internal class ClientPipelineBuilder { private readonly CosmosClient client; private readonly ConsistencyLevel? requestedClientConsistencyLevel; + private readonly DiagnosticsHandler diagnosticsHandler; private readonly RequestHandler invalidPartitionExceptionRetryHandler; private readonly RequestHandler transportHandler; private IReadOnlyCollection customHandlers; @@ -35,6 +36,9 @@ public ClientPipelineBuilder( this.PartitionKeyRangeHandler = new PartitionKeyRangeHandler(client); Debug.Assert(this.PartitionKeyRangeHandler.InnerHandler == null, "The PartitionKeyRangeHandler.InnerHandler must be null to allow other handlers to be linked."); + this.diagnosticsHandler = new DiagnosticsHandler(); + Debug.Assert(this.diagnosticsHandler.InnerHandler == null, nameof(this.diagnosticsHandler)); + this.UseRetryPolicy(); this.AddCustomHandlers(customHandlers); } @@ -129,6 +133,10 @@ public RequestInvokerHandler Build() } } + Debug.Assert(this.diagnosticsHandler != null, nameof(this.diagnosticsHandler)); + current.InnerHandler = this.diagnosticsHandler; + current = current.InnerHandler; + Debug.Assert(this.retryHandler != null, nameof(this.retryHandler)); current.InnerHandler = this.retryHandler; current = current.InnerHandler; diff --git a/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs b/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs new file mode 100644 index 0000000000..3d18ee6705 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Handlers +{ + using System; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Documents.Rntbd; + + /// + /// Handler which add process level info like CPU usage to the + /// diagnostics. This is a best effort scenario. It will not + /// add or attempt to add it if an exception occurs to avoid + /// impacting users. + /// + internal class DiagnosticsHandler : RequestHandler + { + public override Task SendAsync( + RequestMessage request, + CancellationToken cancellationToken) + { + DiagnosticsHandlerHelper.Instance.RecordCpuDiagnostics(request); + return base.SendAsync(request, cancellationToken); + } + + /// + /// This is a helper class that creates a single static instance to avoid each + /// client instance from creating a new CPU monitor. + /// + private class DiagnosticsHandlerHelper + { + public static readonly DiagnosticsHandlerHelper Instance = new DiagnosticsHandlerHelper(); + private readonly CpuMonitor cpuMonitor = null; + private bool isCpuMonitorEnabled = false; + + private DiagnosticsHandlerHelper() + { + // If the CPU monitor fails for some reason don't block the application + try + { + this.cpuMonitor = new CpuMonitor(); + this.cpuMonitor.Start(); + this.isCpuMonitorEnabled = true; + } + catch (Exception) + { + this.isCpuMonitorEnabled = false; + } + } + + /// + /// The diagnostics should never block a request, and is a best attempt + /// If the CPU load history fails then don't try it in the future. + /// + public void RecordCpuDiagnostics(RequestMessage request) + { + if (this.isCpuMonitorEnabled) + { + try + { + CpuLoadHistory cpuHistory = this.cpuMonitor.GetCpuLoad(); + if (cpuHistory != null) + { + request.DiagnosticsContext.AddDiagnosticsInternal(new CosmosSystemInfo(cpuHistory)); + } + } + catch (Exception) + { + this.isCpuMonitorEnabled = false; + } + } + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs index d10534dd1f..a35b1ad34d 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs @@ -283,7 +283,12 @@ public async Task PointOperationDiagnostic(bool disableDiagnostics) if (disableDiagnostics) { requestOptions.DiagnosticContextFactory = () => EmptyCosmosDiagnosticsContext.Singleton; - }; + } + else + { + // Add 10 seconds to ensure CPU history is recorded + await Task.Delay(TimeSpan.FromSeconds(10)); + } //Checking point operation diagnostics on typed operations ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs index e8daaffad4..66a97c770a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs @@ -88,6 +88,18 @@ private static void ValidateScope(CosmosDiagnosticScope scope, TimeSpan? totalEl Assert.IsTrue(elapsedInMs > 0); } + private static void ValidateProcessInfo(CosmosSystemInfo processInfo) + { + Assert.IsNotNull(processInfo.CpuLoadHistory); + Assert.IsNotNull(processInfo.CpuLoadHistory.ToString()); + + string info = processInfo.ToString(); + Assert.IsNotNull(info); + JObject jObject = JObject.Parse(info); + Assert.AreEqual("SystemInfo", jObject["Id"].ToString()); + Assert.IsNotNull(jObject["CpuHistory"].ToString()); + } + private static void ValidateRequestHandlerScope(RequestHandlerScope scope, TimeSpan? totalElapsedTime) { Assert.IsFalse(string.IsNullOrWhiteSpace(scope.Id)); @@ -291,6 +303,11 @@ public override void Visit(RequestHandlerScope requestHandlerScope) // This will be visited if it is gateway query plan } + public override void Visit(CosmosSystemInfo cpuLoadHistory) + { + // This will be visited if it is gateway query plan + } + public override void Visit(QueryPageDiagnostics queryPageDiagnostics) { this.isQueryPageVisited = true; @@ -379,6 +396,12 @@ public override void Visit(RequestHandlerScope requestHandlerScope) DiagnosticValidator.ValidateRequestHandlerScope(requestHandlerScope, this.TotalElapsedTime); } + public override void Visit(CosmosSystemInfo cpuLoadHistory) + { + Assert.IsTrue(this.isContextVisited); + DiagnosticValidator.ValidateProcessInfo(cpuLoadHistory); + } + public override void Visit(CosmosDiagnosticScope cosmosDiagnosticScope) { } @@ -471,6 +494,10 @@ public override void Visit(RequestHandlerScope requestHandlerScope) DiagnosticValidator.ValidateRequestHandlerScope(requestHandlerScope, this.TotalElapsedTime); } + public override void Visit(CosmosSystemInfo cpuLoadHistory) + { + } + public override void Visit(CosmosDiagnosticScope cosmosDiagnosticScope) { } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs index 0ebea78f4b..78150c0bfa 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs @@ -1,363 +1,387 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos.Tests -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.IO; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Runtime.ConstrainedExecution; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Handlers; - using Microsoft.Azure.Cosmos.Scripts; - using Microsoft.Azure.Documents; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Moq; - using Newtonsoft.Json; - - [TestClass] - public class HandlerTests - { - - [TestMethod] - public void HandlerOrder() - { - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); - - Type[] types = new Type[] - { - typeof(RequestInvokerHandler), - typeof(RetryHandler), - typeof(RouterHandler) - }; - - RequestHandler handler = client.RequestHandler; - foreach (Type type in types) - { - Assert.IsTrue(type.Equals(handler.GetType())); - handler = (RequestHandler)handler.InnerHandler; - } - - Assert.IsNull(handler); - } - - [TestMethod] - public async Task TestPreProcessingHandler() - { - RequestHandler preProcessHandler = new PreProcessingTestHandler(); - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient((builder) => builder.AddCustomHandlers(preProcessHandler)); - - Assert.IsTrue(typeof(RequestInvokerHandler).Equals(client.RequestHandler.GetType())); - Assert.IsTrue(typeof(PreProcessingTestHandler).Equals(client.RequestHandler.InnerHandler.GetType())); - - Container container = client.GetDatabase("testdb") - .GetContainer("testcontainer"); - - HttpStatusCode[] testHttpStatusCodes = new HttpStatusCode[] - { - HttpStatusCode.OK - }; - - // User operations - foreach (HttpStatusCode code in testHttpStatusCodes) - { - ItemRequestOptions options = new ItemRequestOptions(); - options.Properties = new Dictionary() +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Tests +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Handlers; + using Microsoft.Azure.Cosmos.Scripts; + using Microsoft.Azure.Documents; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json; + + [TestClass] + public class HandlerTests + { + + [TestMethod] + public void HandlerOrder() + { + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); + + Type[] types = new Type[] + { + typeof(RequestInvokerHandler), + typeof(DiagnosticsHandler), + typeof(RetryHandler), + typeof(RouterHandler) + }; + + RequestHandler handler = client.RequestHandler; + foreach (Type type in types) + { + Assert.IsTrue(type.Equals(handler.GetType())); + handler = handler.InnerHandler; + } + + Assert.IsNull(handler); + } + + [TestMethod] + public async Task TestPreProcessingHandler() + { + RequestHandler preProcessHandler = new PreProcessingTestHandler(); + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient((builder) => builder.AddCustomHandlers(preProcessHandler)); + + Assert.IsTrue(typeof(RequestInvokerHandler).Equals(client.RequestHandler.GetType())); + Assert.IsTrue(typeof(PreProcessingTestHandler).Equals(client.RequestHandler.InnerHandler.GetType())); + + Container container = client.GetDatabase("testdb") + .GetContainer("testcontainer"); + + HttpStatusCode[] testHttpStatusCodes = new HttpStatusCode[] + { + HttpStatusCode.OK + }; + + // User operations + foreach (HttpStatusCode code in testHttpStatusCodes) + { + ItemRequestOptions options = new ItemRequestOptions + { + Properties = new Dictionary() { { PreProcessingTestHandler.StatusCodeName, code }, - }; - - ItemResponse response = await container.ReadItemAsync("id1", new Cosmos.PartitionKey("pk1"), options); - Console.WriteLine($"Got status code {response.StatusCode}"); - Assert.AreEqual(code, response.StatusCode); - } - - // Meta-data operations - foreach (HttpStatusCode code in testHttpStatusCodes) - { - ContainerRequestOptions options = new ContainerRequestOptions(); - options.Properties = new Dictionary() + } + }; + + ItemResponse response = await container.ReadItemAsync("id1", new Cosmos.PartitionKey("pk1"), options); + Console.WriteLine($"Got status code {response.StatusCode}"); + Assert.AreEqual(code, response.StatusCode); + } + + // Meta-data operations + foreach (HttpStatusCode code in testHttpStatusCodes) + { + ContainerRequestOptions options = new ContainerRequestOptions + { + Properties = new Dictionary() { { PreProcessingTestHandler.StatusCodeName, code } - }; - - ContainerResponse response = await container.DeleteContainerAsync(options); - - Console.WriteLine($"Got status code {response.StatusCode}"); - Assert.AreEqual(code, response.StatusCode); - - } - } - - [TestMethod] - public async Task RequestOptionsHandlerCanHandleRequestOptions() - { - const string PropertyKey = "propkey"; - const string Condition = "*"; - object propertyValue = Encoding.UTF8.GetBytes("test"); - RequestOptions options = new ItemRequestOptions - { - Properties = new Dictionary(new List> { - new KeyValuePair(PropertyKey, propertyValue) - }), - IfMatchEtag = Condition, - }; - - TestHandler testHandler = new TestHandler((request, cancellationToken) => - { - Assert.AreEqual(propertyValue, request.Properties[PropertyKey]); - Assert.AreEqual(Condition, request.Headers.GetValues(HttpConstants.HttpHeaders.IfMatch).First()); - return TestHandler.ReturnSuccess(); - }); - - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); - - RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null); - invoker.InnerHandler = testHandler; - RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); - requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); - requestMessage.ResourceType = ResourceType.Document; - requestMessage.OperationType = OperationType.Read; - requestMessage.RequestOptions = options; - - await invoker.SendAsync(requestMessage, new CancellationToken()); - } - - [TestMethod] - public async Task RequestOptionsConsistencyLevel() - { - List cosmosLevels = Enum.GetValues(typeof(Cosmos.ConsistencyLevel)).Cast().ToList(); - List documentLevels = Enum.GetValues(typeof(Documents.ConsistencyLevel)).Cast().ToList(); - CollectionAssert.AreEqual(cosmosLevels, documentLevels, new EnumComparer(), "Document consistency level is different from cosmos consistency level"); - - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(accountConsistencyLevel: Cosmos.ConsistencyLevel.Strong); - - foreach (Cosmos.ConsistencyLevel level in cosmosLevels) - { - List requestOptions = new List(); - requestOptions.Add(new ItemRequestOptions - { - ConsistencyLevel = level - }); - - requestOptions.Add(new QueryRequestOptions - { - ConsistencyLevel = level - }); - - requestOptions.Add(new StoredProcedureRequestOptions - { - ConsistencyLevel = level - }); - - foreach (RequestOptions option in requestOptions) - { - TestHandler testHandler = new TestHandler((request, cancellationToken) => - { - Assert.AreEqual(level.ToString(), request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel]); - return TestHandler.ReturnSuccess(); - }); - - RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null); - invoker.InnerHandler = testHandler; - - RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); - requestMessage.ResourceType = ResourceType.Document; - requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); - requestMessage.OperationType = OperationType.Read; - requestMessage.RequestOptions = option; - - await invoker.SendAsync(requestMessage, new CancellationToken()); - } - } + } + }; + + ContainerResponse response = await container.DeleteContainerAsync(options); + + Console.WriteLine($"Got status code {response.StatusCode}"); + Assert.AreEqual(code, response.StatusCode); + + } } - [TestMethod] - public async Task QueryRequestOptionsSessionToken() - { - const string SessionToken = "SessionToken"; - ItemRequestOptions options = new ItemRequestOptions - { - SessionToken = SessionToken - }; - - TestHandler testHandler = new TestHandler((request, cancellationToken) => - { - Assert.AreEqual(SessionToken, request.Headers.GetValues(HttpConstants.HttpHeaders.SessionToken).First()); - return TestHandler.ReturnSuccess(); - }); - - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); - - RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null); - invoker.InnerHandler = testHandler; - RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); - requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); - requestMessage.ResourceType = ResourceType.Document; - requestMessage.OperationType = OperationType.Read; - requestMessage.RequestOptions = options; - await invoker.SendAsync(requestMessage, new CancellationToken()); + [TestMethod] + public async Task RequestOptionsHandlerCanHandleRequestOptions() + { + const string PropertyKey = "propkey"; + const string Condition = "*"; + object propertyValue = Encoding.UTF8.GetBytes("test"); + RequestOptions options = new ItemRequestOptions + { + Properties = new Dictionary(new List> { + new KeyValuePair(PropertyKey, propertyValue) + }), + IfMatchEtag = Condition, + }; + + TestHandler testHandler = new TestHandler((request, cancellationToken) => + { + Assert.AreEqual(propertyValue, request.Properties[PropertyKey]); + Assert.AreEqual(Condition, request.Headers.GetValues(HttpConstants.HttpHeaders.IfMatch).First()); + return TestHandler.ReturnSuccess(); + }); + + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); + + RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null) + { + InnerHandler = testHandler + }; + RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); + requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); + requestMessage.ResourceType = ResourceType.Document; + requestMessage.OperationType = OperationType.Read; + requestMessage.RequestOptions = options; + + await invoker.SendAsync(requestMessage, new CancellationToken()); + } + + [TestMethod] + public async Task RequestOptionsConsistencyLevel() + { + List cosmosLevels = Enum.GetValues(typeof(Cosmos.ConsistencyLevel)).Cast().ToList(); + List documentLevels = Enum.GetValues(typeof(Documents.ConsistencyLevel)).Cast().ToList(); + CollectionAssert.AreEqual(cosmosLevels, documentLevels, new EnumComparer(), "Document consistency level is different from cosmos consistency level"); + + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(accountConsistencyLevel: Cosmos.ConsistencyLevel.Strong); + + foreach (Cosmos.ConsistencyLevel level in cosmosLevels) + { + List requestOptions = new List + { + new ItemRequestOptions + { + ConsistencyLevel = level + }, + + new QueryRequestOptions + { + ConsistencyLevel = level + }, + + new StoredProcedureRequestOptions + { + ConsistencyLevel = level + } + }; + + foreach (RequestOptions option in requestOptions) + { + TestHandler testHandler = new TestHandler((request, cancellationToken) => + { + Assert.AreEqual(level.ToString(), request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel]); + return TestHandler.ReturnSuccess(); + }); + + RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null) + { + InnerHandler = testHandler + }; + + RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")) + { + ResourceType = ResourceType.Document + }; + requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); + requestMessage.OperationType = OperationType.Read; + requestMessage.RequestOptions = option; + + await invoker.SendAsync(requestMessage, new CancellationToken()); + } + } + } + + [TestMethod] + public async Task QueryRequestOptionsSessionToken() + { + const string SessionToken = "SessionToken"; + ItemRequestOptions options = new ItemRequestOptions + { + SessionToken = SessionToken + }; + + TestHandler testHandler = new TestHandler((request, cancellationToken) => + { + Assert.AreEqual(SessionToken, request.Headers.GetValues(HttpConstants.HttpHeaders.SessionToken).First()); + return TestHandler.ReturnSuccess(); + }); + + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); + + RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null) + { + InnerHandler = testHandler + }; + RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); + requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); + requestMessage.ResourceType = ResourceType.Document; + requestMessage.OperationType = OperationType.Read; + requestMessage.RequestOptions = options; + await invoker.SendAsync(requestMessage, new CancellationToken()); + } + + [TestMethod] + public async Task ConsistencyLevelClient() + { + Cosmos.ConsistencyLevel clientLevel = Cosmos.ConsistencyLevel.Eventual; + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient( + accountConsistencyLevel: Cosmos.ConsistencyLevel.Strong, + customizeClientBuilder: builder => builder.WithConsistencyLevel(clientLevel)); + + TestHandler testHandler = new TestHandler((request, cancellationToken) => + { + Assert.AreEqual(clientLevel.ToString(), request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel]); + return TestHandler.ReturnSuccess(); + }); + + RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: client.ClientOptions.ConsistencyLevel) + { + InnerHandler = testHandler + }; + + RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")) + { + ResourceType = ResourceType.Document + }; + requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); + requestMessage.OperationType = OperationType.Read; + + await invoker.SendAsync(requestMessage, new CancellationToken()); + } + + [TestMethod] + public async Task ConsistencyLevelClientAndRequestOption() + { + Cosmos.ConsistencyLevel requestOptionLevel = Cosmos.ConsistencyLevel.BoundedStaleness; + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient( + accountConsistencyLevel: Cosmos.ConsistencyLevel.Strong, + customizeClientBuilder: builder => builder.WithConsistencyLevel(Cosmos.ConsistencyLevel.Eventual)); + + TestHandler testHandler = new TestHandler((request, cancellationToken) => + { + Assert.AreEqual(requestOptionLevel.ToString(), request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel]); + return TestHandler.ReturnSuccess(); + }); + + RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null) + { + InnerHandler = testHandler + }; + + RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")) + { + ResourceType = ResourceType.Document + }; + requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); + requestMessage.OperationType = OperationType.Read; + requestMessage.RequestOptions = new ItemRequestOptions() { ConsistencyLevel = requestOptionLevel }; + + await invoker.SendAsync(requestMessage, new CancellationToken()); + } + + [TestMethod] + public async Task RequestOptionsHandlerCanHandleDataPlaneRequestOptions() + { + const string Condition = "*"; + const string SessionToken = "test"; + ItemRequestOptions options = new ItemRequestOptions + { + IfNoneMatchEtag = Condition, + ConsistencyLevel = (Cosmos.ConsistencyLevel)ConsistencyLevel.Eventual, + SessionToken = SessionToken + }; + + TestHandler testHandler = new TestHandler((request, cancellationToken) => + { + Assert.AreEqual(Condition, request.Headers.GetValues(HttpConstants.HttpHeaders.IfNoneMatch).First()); + Assert.AreEqual(ConsistencyLevel.Eventual.ToString(), request.Headers.GetValues(HttpConstants.HttpHeaders.ConsistencyLevel).First()); + Assert.AreEqual(SessionToken, request.Headers.GetValues(HttpConstants.HttpHeaders.SessionToken).First()); + return TestHandler.ReturnSuccess(); + }); + + CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); + + RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null) + { + InnerHandler = testHandler + }; + RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); + requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); + requestMessage.ResourceType = ResourceType.Document; + requestMessage.OperationType = OperationType.Read; + requestMessage.RequestOptions = options; + await invoker.SendAsync(requestMessage, new CancellationToken()); + } + + [TestMethod] + public async Task Test() + { + SomePayload t = new SomePayload() + { + V1 = "Value1", + V2 = "Value2", + }; + + JsonSerializer js = new JsonSerializer(); + using (MemoryStream ms = new MemoryStream()) + { + StreamWriter sw = new StreamWriter(ms); + JsonTextWriter tw = new JsonTextWriter(sw); + js.Serialize(tw, t); + + ms.Seek(0, SeekOrigin.Begin); + + HttpMethod method = HttpMethod.Get; + string ep = "https://httpbin.org/put"; + HttpRequestMessage hrm = new HttpRequestMessage(method, ep) + { + Content = new StreamContent(ms) + }; + + for (int i = 0; i < 5; i++) + { + using (MemoryStream msCopy = new MemoryStream()) + { + await hrm.Content.CopyToAsync(msCopy); + } + } + } + } + + [TestMethod] + public void TestAggregateExceptionConverter() + { + string errorMessage = "BadRequest message"; + IEnumerable exceptions = new List() + { + new DocumentClientException(errorMessage, innerException: null, statusCode: HttpStatusCode.BadRequest) + }; + + AggregateException ae = new AggregateException(message: "Test AE message", innerExceptions: exceptions); + + ResponseMessage response = TransportHandler.AggregateExceptionConverter(ae, null); + Assert.IsNotNull(response); + Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); + Assert.IsTrue(response.ErrorMessage.Contains(errorMessage)); + } + + private class SomePayload + { + public string V1 { get; set; } + public string V2 { get; set; } + } + + private class EnumComparer : IComparer + { + public int Compare(object x, object y) + { + if ((int)x == (int)y && + string.Equals(x.ToString(), y.ToString())) + { + return 0; + } + + return 1; + } } - - [TestMethod] - public async Task ConsistencyLevelClient() - { - Cosmos.ConsistencyLevel clientLevel = Cosmos.ConsistencyLevel.Eventual; - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient( - accountConsistencyLevel: Cosmos.ConsistencyLevel.Strong, - customizeClientBuilder: builder => builder.WithConsistencyLevel(clientLevel)); - - TestHandler testHandler = new TestHandler((request, cancellationToken) => - { - Assert.AreEqual(clientLevel.ToString(), request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel]); - return TestHandler.ReturnSuccess(); - }); - - RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: client.ClientOptions.ConsistencyLevel); - invoker.InnerHandler = testHandler; - - RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); - requestMessage.ResourceType = ResourceType.Document; - requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); - requestMessage.OperationType = OperationType.Read; - - await invoker.SendAsync(requestMessage, new CancellationToken()); - } - - [TestMethod] - public async Task ConsistencyLevelClientAndRequestOption() - { - Cosmos.ConsistencyLevel requestOptionLevel = Cosmos.ConsistencyLevel.BoundedStaleness; - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient( - accountConsistencyLevel: Cosmos.ConsistencyLevel.Strong, - customizeClientBuilder: builder => builder.WithConsistencyLevel(Cosmos.ConsistencyLevel.Eventual)); - - TestHandler testHandler = new TestHandler((request, cancellationToken) => - { - Assert.AreEqual(requestOptionLevel.ToString(), request.Headers[HttpConstants.HttpHeaders.ConsistencyLevel]); - return TestHandler.ReturnSuccess(); - }); - - RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null); - invoker.InnerHandler = testHandler; - - RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); - requestMessage.ResourceType = ResourceType.Document; - requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); - requestMessage.OperationType = OperationType.Read; - requestMessage.RequestOptions = new ItemRequestOptions() { ConsistencyLevel = requestOptionLevel }; - - await invoker.SendAsync(requestMessage, new CancellationToken()); - } - - [TestMethod] - public async Task RequestOptionsHandlerCanHandleDataPlaneRequestOptions() - { - const string Condition = "*"; - const string SessionToken = "test"; - ItemRequestOptions options = new ItemRequestOptions - { - IfNoneMatchEtag = Condition, - ConsistencyLevel = (Cosmos.ConsistencyLevel)ConsistencyLevel.Eventual, - SessionToken = SessionToken - }; - - TestHandler testHandler = new TestHandler((request, cancellationToken) => - { - Assert.AreEqual(Condition, request.Headers.GetValues(HttpConstants.HttpHeaders.IfNoneMatch).First()); - Assert.AreEqual(ConsistencyLevel.Eventual.ToString(), request.Headers.GetValues(HttpConstants.HttpHeaders.ConsistencyLevel).First()); - Assert.AreEqual(SessionToken, request.Headers.GetValues(HttpConstants.HttpHeaders.SessionToken).First()); - return TestHandler.ReturnSuccess(); - }); - - CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); - - RequestInvokerHandler invoker = new RequestInvokerHandler(client, requestedClientConsistencyLevel: null); - invoker.InnerHandler = testHandler; - RequestMessage requestMessage = new RequestMessage(HttpMethod.Get, new System.Uri("https://dummy.documents.azure.com:443/dbs")); - requestMessage.Headers.Add(HttpConstants.HttpHeaders.PartitionKey, "[]"); - requestMessage.ResourceType = ResourceType.Document; - requestMessage.OperationType = OperationType.Read; - requestMessage.RequestOptions = options; - await invoker.SendAsync(requestMessage, new CancellationToken()); - } - - [TestMethod] - public async Task Test() - { - SomePayload t = new SomePayload() - { - V1 = "Value1", - V2 = "Value2", - }; - - JsonSerializer js = new JsonSerializer(); - using (MemoryStream ms = new MemoryStream()) - { - StreamWriter sw = new StreamWriter(ms); - JsonTextWriter tw = new JsonTextWriter(sw); - js.Serialize(tw, t); - - ms.Seek(0, SeekOrigin.Begin); - - HttpMethod method = HttpMethod.Get; - string ep = "https://httpbin.org/put"; - HttpRequestMessage hrm = new HttpRequestMessage(method, ep); - hrm.Content = new StreamContent(ms); - - for (int i = 0; i < 5; i++) - { - using (MemoryStream msCopy = new MemoryStream()) - { - await hrm.Content.CopyToAsync(msCopy); - } - } - } - } - - [TestMethod] - public void TestAggregateExceptionConverter() - { - string errorMessage = "BadRequest message"; - IEnumerable exceptions = new List() - { - new DocumentClientException(errorMessage, innerException: null, statusCode: HttpStatusCode.BadRequest) - }; - - AggregateException ae = new AggregateException(message: "Test AE message", innerExceptions: exceptions); - - ResponseMessage response = TransportHandler.AggregateExceptionConverter(ae, null); - Assert.IsNotNull(response); - Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - Assert.IsTrue(response.ErrorMessage.Contains(errorMessage)); - } - - private class SomePayload - { - public string V1 { get; set; } - public string V2 { get; set; } - } - - private class EnumComparer : IComparer - { - public int Compare(object x, object y) - { - if ((int)x == (int)y && - string.Equals(x.ToString(), y.ToString())) - { - return 0; - } - - return 1; - } - } - } -} + } +}