From 76e76dd5f51bdae84927bc3490e2b7903982c38a Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Fri, 30 Aug 2019 19:58:06 -0400 Subject: [PATCH] Adding basic request diagnostics for V3 (#615) * inital commit * making cosmosDiagnostic public * bumping direct version * removing formatting of json * removing extra line * updating per new changes with clientsiderequestdiagnostics * using to string on CLientSideRequestDiagnostics * updating DOtNetSDKAPI * updating DotNetSDKAPI.json * moving cosmosDiagnostic as abstract clas * adding abstract tostring * updating change log * removing query metrics from header * updating contract * upgrading direct to 3.1.6 to leverage IClientSideRequestDiagnostics * changing name of cosmosDiagnostics to diagnostics * moving diagnostics to Diagnostics --- .../src/ClientRetryPolicy.cs | 4 +- .../src/DocumentFeedResponse.cs | 6 +- .../src/Handler/ResponseMessage.cs | 5 + .../CosmosQueryResponseMessageHeaders.cs | 2 + Microsoft.Azure.Cosmos/src/Headers/Headers.cs | 3 + .../src/Microsoft.Azure.Cosmos.csproj | 2 +- ...smosCrossPartitionQueryExecutionContext.cs | 9 +- .../CosmosQueryExecutionContextFactory.cs | 7 +- ...ggregateDocumentQueryExecutionComponent.cs | 9 +- ...DistinctDocumentQueryExecutionComponent.cs | 3 +- .../SkipDocumentQueryExecutionComponent.cs | 1 + .../TakeDocumentQueryExecutionComponent.cs | 3 +- .../PipelinedDocumentQueryExecutionContext.cs | 3 +- .../src/Query/QueryMetrics.cs | 18 +- .../src/RequestOptions/QueryRequestOptions.cs | 4 +- .../src/Resource/Container/ItemResponse.cs | 4 +- .../src/Resource/CosmosResponseFactory.cs | 5 +- .../Resource/QueryResponses/QueryResponse.cs | 28 ++- .../src/Resource/Response.cs | 5 + .../CosmosClientSideRequestStatistics.cs | 184 ++++++++++++++++++ .../Resource/Settings/CosmosDiagnostics.cs | 18 ++ .../Settings/PointOperationStatistics.cs | 59 ++++++ .../Settings/QueryOperationStatistics.cs | 24 +++ .../src/StoredProcedureResponse.cs | 2 +- Microsoft.Azure.Cosmos/src/Util/Extensions.cs | 9 + .../CosmosDiagnosticsTests.cs | 152 +++++++++++++++ ...icrosoft.Azure.Cosmos.EmulatorTests.csproj | 2 +- ...soft.Azure.Cosmos.Performance.Tests.csproj | 2 +- .../DotNetSDKAPI.json | 49 +++++ .../Microsoft.Azure.Cosmos.Tests.csproj | 2 +- .../PointOperationStatisticsTest.cs | 65 +++++++ changelog.md | 1 + 32 files changed, 634 insertions(+), 56 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosClientSideRequestStatistics.cs create mode 100644 Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosDiagnostics.cs create mode 100644 Microsoft.Azure.Cosmos/src/Resource/Settings/PointOperationStatistics.cs create mode 100644 Microsoft.Azure.Cosmos/src/Resource/Settings/QueryOperationStatistics.cs create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PointOperationStatisticsTest.cs diff --git a/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs index 77c8f565cc..32d74fb26a 100644 --- a/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs @@ -33,7 +33,7 @@ internal sealed class ClientRetryPolicy : IDocumentClientRetryPolicy private Uri locationEndpoint; private RetryContext retryContext; - private ClientSideRequestStatistics sharedStatistics; + private IClientSideRequestStatistics sharedStatistics; public ClientRetryPolicy( GlobalEndpointManager globalEndpointManager, @@ -50,7 +50,7 @@ public ClientRetryPolicy( this.sessionTokenRetryCount = 0; this.canUseMultipleWriteLocations = false; - this.sharedStatistics = new ClientSideRequestStatistics(); + this.sharedStatistics = new CosmosClientSideRequestStatistics(); } /// diff --git a/Microsoft.Azure.Cosmos/src/DocumentFeedResponse.cs b/Microsoft.Azure.Cosmos/src/DocumentFeedResponse.cs index 494f34c92b..647059e590 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentFeedResponse.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentFeedResponse.cs @@ -53,7 +53,7 @@ internal DocumentFeedResponse( INameValueCollection responseHeaders, bool useETagAsContinuation = false, IReadOnlyDictionary queryMetrics = null, - ClientSideRequestStatistics requestStats = null, + IClientSideRequestStatistics requestStats = null, string disallowContinuationTokenMessage = null, long responseLengthBytes = 0) : this(result) @@ -79,7 +79,7 @@ internal DocumentFeedResponse( IEnumerable result, int count, INameValueCollection responseHeaders, - ClientSideRequestStatistics requestStats, + IClientSideRequestStatistics requestStats, long responseLengthBytes) : this(result, count, responseHeaders, false, null, requestStats, responseLengthBytes: responseLengthBytes) { @@ -91,7 +91,7 @@ internal DocumentFeedResponse( /// /// This value is currently used for tracking replica Uris. /// - internal ClientSideRequestStatistics RequestStatistics { get; private set; } + internal IClientSideRequestStatistics RequestStatistics { get; private set; } /// /// Gets the response length in bytes diff --git a/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs b/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs index 0a28e14766..60b05795ca 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs @@ -108,6 +108,11 @@ public virtual Stream Content /// public virtual RequestMessage RequestMessage { get; internal set; } + /// + /// Gets the cosmos diagnostic information for the current request to Azure Cosmos DB service + /// + public CosmosDiagnostics Diagnostics { get; set; } + /// /// Gets the internal error object. /// diff --git a/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs b/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs index 0b9eddbc78..b93ba01664 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs @@ -75,6 +75,7 @@ internal CosmosQueryResponseMessageHeaders CloneKnownProperties( RetryAfterLiteral = this.RetryAfterLiteral, SubStatusCodeLiteral = this.SubStatusCodeLiteral, ContentType = this.ContentType, + QueryMetricsText = QueryMetricsText }; } @@ -106,6 +107,7 @@ internal static CosmosQueryResponseMessageHeaders ConvertToQueryHeaders( RetryAfterLiteral = sourceHeaders.RetryAfterLiteral, SubStatusCodeLiteral = sourceHeaders.SubStatusCodeLiteral, ContentType = sourceHeaders.ContentType, + QueryMetricsText = sourceHeaders.QueryMetricsText }; } } diff --git a/Microsoft.Azure.Cosmos/src/Headers/Headers.cs b/Microsoft.Azure.Cosmos/src/Headers/Headers.cs index 38a14a2678..b5413b735e 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/Headers.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/Headers.cs @@ -167,6 +167,9 @@ internal string RetryAfterLiteral [CosmosKnownHeaderAttribute(HeaderName = HttpConstants.HttpHeaders.PageSize)] internal string PageSize { get; set; } + [CosmosKnownHeaderAttribute(HeaderName = HttpConstants.HttpHeaders.QueryMetrics)] + internal string QueryMetricsText { get; set; } + /// /// Creates a new instance of . /// diff --git a/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj b/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj index fbe2d6fec4..282989ee3c 100644 --- a/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj +++ b/Microsoft.Azure.Cosmos/src/Microsoft.Azure.Cosmos.csproj @@ -7,7 +7,7 @@ © Microsoft Corporation. All rights reserved. en-US 3.1.1 - 3.1.5 + 3.1.6 1.0.0-preview Microsoft.Azure.Cosmos.Direct Microsoft.Azure.Cosmos.Direct.MyGet diff --git a/Microsoft.Azure.Cosmos/src/Query/CosmosCrossPartitionQueryExecutionContext.cs b/Microsoft.Azure.Cosmos/src/Query/CosmosCrossPartitionQueryExecutionContext.cs index 18b6ae7929..0896b2c0cf 100644 --- a/Microsoft.Azure.Cosmos/src/Query/CosmosCrossPartitionQueryExecutionContext.cs +++ b/Microsoft.Azure.Cosmos/src/Query/CosmosCrossPartitionQueryExecutionContext.cs @@ -236,14 +236,6 @@ public CosmosQueryResponseMessageHeaders GetResponseHeaders() this.SetQueryMetrics(); - IReadOnlyDictionary groupedQueryMetrics = this.GetQueryMetrics(); - if (groupedQueryMetrics != null && groupedQueryMetrics.Count != 0) - { - responseHeaders[HttpConstants.HttpHeaders.QueryMetrics] = QueryMetrics - .CreateFromIEnumerable(groupedQueryMetrics.Values) - .ToDelimitedString(); - } - responseHeaders.RequestCharge = this.requestChargeTracker.GetAndResetCharge(); return responseHeaders; @@ -392,6 +384,7 @@ public override async Task DrainAsync(int maxElements, Cancellati result: results, count: results.Count, responseHeaders: this.GetResponseHeaders(), + queryMetrics: this.GetQueryMetrics(), responseLengthBytes: this.GetAndResetResponseLengthBytes()); } diff --git a/Microsoft.Azure.Cosmos/src/Query/CosmosQueryExecutionContextFactory.cs b/Microsoft.Azure.Cosmos/src/Query/CosmosQueryExecutionContextFactory.cs index ae65ad1342..25e9a6d453 100644 --- a/Microsoft.Azure.Cosmos/src/Query/CosmosQueryExecutionContextFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/CosmosQueryExecutionContextFactory.cs @@ -199,6 +199,11 @@ private async Task ExecuteNextHelperAsync(CancellationToken can } } + if (response?.queryMetrics != null && response?.queryMetrics.Count > 0) + { + response.Diagnostics = new QueryOperationStatistics(response.queryMetrics); + } + return response; } @@ -307,7 +312,7 @@ private async Task CreateItemQueryExecutionContextA rewrittenComosQueryContext = this.cosmosQueryContext; } - return await CreateSpecializedDocumentQueryExecutionContextAsync( + return await this.CreateSpecializedDocumentQueryExecutionContextAsync( rewrittenComosQueryContext, partitionedQueryExecutionInfo, targetRanges, diff --git a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/AggregateDocumentQueryExecutionComponent.cs b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/AggregateDocumentQueryExecutionComponent.cs index 14de9010a9..0e3fad7766 100644 --- a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/AggregateDocumentQueryExecutionComponent.cs +++ b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/AggregateDocumentQueryExecutionComponent.cs @@ -150,13 +150,14 @@ public override async Task DrainAsync(int maxElements, Cancellati count: finalResult.Count, responseLengthBytes: responseLengthBytes, responseHeaders: new CosmosQueryResponseMessageHeaders( - continauationToken: null, - disallowContinuationTokenMessage: null, - resourceType: resourceType, + continauationToken: null, + disallowContinuationTokenMessage: null, + resourceType: resourceType, containerRid: containerRid) { RequestCharge = requestCharge - }); + }, + queryMetrics: this.GetQueryMetrics()); } /// diff --git a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/DistinctDocumentQueryExecutionComponent.cs b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/DistinctDocumentQueryExecutionComponent.cs index c9db1f83c7..f2191a448a 100644 --- a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/DistinctDocumentQueryExecutionComponent.cs +++ b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/DistinctDocumentQueryExecutionComponent.cs @@ -133,7 +133,8 @@ public override async Task DrainAsync(int maxElements, Cancellati distinctResults, distinctResults.Count, cosmosQueryResponse.ResponseLengthBytes, - cosmosQueryResponse.QueryHeaders.CloneKnownProperties(updatedContinuationToken, disallowContinuationTokenMessage)); + cosmosQueryResponse.QueryHeaders.CloneKnownProperties(updatedContinuationToken, disallowContinuationTokenMessage), + cosmosQueryResponse.queryMetrics); } /// diff --git a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/SkipDocumentQueryExecutionComponent.cs b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/SkipDocumentQueryExecutionComponent.cs index 3f3115822b..364509af0a 100644 --- a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/SkipDocumentQueryExecutionComponent.cs +++ b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/SkipDocumentQueryExecutionComponent.cs @@ -88,6 +88,7 @@ public override async Task DrainAsync(int maxElements, Cancellati result: documentsAfterSkip, count: documentsAfterSkip.Count(), responseHeaders: sourcePage.QueryHeaders.CloneKnownProperties(updatedContinuationToken, sourcePage.QueryHeaders.DisallowContinuationTokenMessage), + queryMetrics: sourcePage.queryMetrics, responseLengthBytes: sourcePage.ResponseLengthBytes); } diff --git a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/TakeDocumentQueryExecutionComponent.cs b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/TakeDocumentQueryExecutionComponent.cs index e99dbff771..25b6876d80 100644 --- a/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/TakeDocumentQueryExecutionComponent.cs +++ b/Microsoft.Azure.Cosmos/src/Query/ExecutionComponent/TakeDocumentQueryExecutionComponent.cs @@ -139,7 +139,8 @@ public override async Task DrainAsync(int maxElements, Cancellati takedDocuments, takedDocuments.Count, results.ResponseLengthBytes, - results.QueryHeaders.CloneKnownProperties(updatedContinuationToken, results.QueryHeaders.DisallowContinuationTokenMessage)); + results.QueryHeaders.CloneKnownProperties(updatedContinuationToken, results.QueryHeaders.DisallowContinuationTokenMessage), + results.queryMetrics); } private enum TakeEnum diff --git a/Microsoft.Azure.Cosmos/src/Query/PipelinedDocumentQueryExecutionContext.cs b/Microsoft.Azure.Cosmos/src/Query/PipelinedDocumentQueryExecutionContext.cs index e44998f895..c2b58501a3 100644 --- a/Microsoft.Azure.Cosmos/src/Query/PipelinedDocumentQueryExecutionContext.cs +++ b/Microsoft.Azure.Cosmos/src/Query/PipelinedDocumentQueryExecutionContext.cs @@ -426,7 +426,8 @@ public override async Task ExecuteNextAsync(CancellationToken tok dynamics, queryResponse.Count, queryResponse.ResponseLengthBytes, - queryResponse.QueryHeaders.CloneKnownProperties()); + queryResponse.QueryHeaders.CloneKnownProperties(), + queryMetrics: queryResponse.queryMetrics); } catch (Exception) { diff --git a/Microsoft.Azure.Cosmos/src/Query/QueryMetrics.cs b/Microsoft.Azure.Cosmos/src/Query/QueryMetrics.cs index ffe3992094..13b3cf49b0 100644 --- a/Microsoft.Azure.Cosmos/src/Query/QueryMetrics.cs +++ b/Microsoft.Azure.Cosmos/src/Query/QueryMetrics.cs @@ -157,7 +157,7 @@ public long OutputDocumentCount /// /// Gets the size of documents outputted in bytes during query in the Azure DocumentDB database service. /// - internal long OutputDocumentSize + public long OutputDocumentSize { get { @@ -168,7 +168,7 @@ internal long OutputDocumentSize /// /// Gets the total query time in the Azure DocumentDB database service. /// - internal TimeSpan TotalQueryExecutionTime + public TimeSpan TotalQueryExecutionTime { get { @@ -212,7 +212,7 @@ public long Retries /// /// Gets the query index lookup time in the Azure DocumentDB database service. /// - internal TimeSpan IndexLookupTime + public TimeSpan IndexLookupTime { get { @@ -223,7 +223,7 @@ internal TimeSpan IndexLookupTime /// /// Gets the document loading time during query in the Azure DocumentDB database service. /// - internal TimeSpan DocumentLoadTime + public TimeSpan DocumentLoadTime { get { @@ -234,7 +234,7 @@ internal TimeSpan DocumentLoadTime /// /// Gets the query runtime execution times during query in the Azure DocumentDB database service. /// - internal RuntimeExecutionTimes RuntimeExecutionTimes + public RuntimeExecutionTimes RuntimeExecutionTimes { get { @@ -245,7 +245,7 @@ internal RuntimeExecutionTimes RuntimeExecutionTimes /// /// Gets the output writing/serializing time during query in the Azure DocumentDB database service. /// - internal TimeSpan DocumentWriteTime + public TimeSpan DocumentWriteTime { get { @@ -281,7 +281,7 @@ public double IndexHitRatio /// /// Gets the Index Hit Document Count. /// - internal long IndexHitDocumentCount + public long IndexHitDocumentCount { get { @@ -292,7 +292,7 @@ internal long IndexHitDocumentCount /// /// Gets the VMExecution Time. /// - internal TimeSpan VMExecutionTime + public TimeSpan VMExecutionTime { get { @@ -560,7 +560,7 @@ public TimeSpan WriteOutputTime /// /// Gets the query runtime execution times during query in the Azure DocumentDB database service. /// - internal RuntimeExecutionTimes RuntimeExecutionTimes + public RuntimeExecutionTimes RuntimeExecutionTimes { get { diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs index 56391edc9c..79c0cba3ba 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs @@ -198,6 +198,8 @@ internal override void PopulateRequestOptions(RequestMessage request) request.Headers.Add(HttpConstants.HttpHeaders.ContentSerializationFormat, this.CosmosSerializationOptions.ContentSerializationFormat); } + request.Headers.Add(HttpConstants.HttpHeaders.PopulateQueryMetrics, bool.TrueString); + base.PopulateRequestOptions(request); } @@ -220,7 +222,7 @@ internal QueryRequestOptions Clone() EnableCrossPartitionSkipTake = this.EnableCrossPartitionSkipTake, EnableGroupBy = this.EnableGroupBy, Properties = this.Properties, - IsEffectivePartitionKeyRouting = this.IsEffectivePartitionKeyRouting + IsEffectivePartitionKeyRouting = this.IsEffectivePartitionKeyRouting, }; return queryRequestOptions; diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/ItemResponse.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/ItemResponse.cs index 208a7ab227..8884de44ec 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/ItemResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/ItemResponse.cs @@ -27,11 +27,13 @@ protected ItemResponse() internal ItemResponse( HttpStatusCode httpStatusCode, Headers headers, - T item) + T item, + CosmosDiagnostics diagnostics) { this.StatusCode = httpStatusCode; this.Headers = headers; this.Resource = item; + this.Diagnostics = diagnostics; } /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosResponseFactory.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosResponseFactory.cs index e2e21a2c74..8700869308 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosResponseFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosResponseFactory.cs @@ -38,7 +38,7 @@ internal FeedResponse CreateQueryFeedResponse( if (queryResponse != null) { return QueryResponse.CreateResponse( - responseMessage: queryResponse, + cosmosQueryResponse: queryResponse, jsonSerializer: this.cosmosSerializer); } @@ -56,7 +56,8 @@ internal Task> CreateItemResponseAsync( return new ItemResponse( cosmosResponseMessage.StatusCode, cosmosResponseMessage.Headers, - item); + item, + cosmosResponseMessage.Diagnostics); }); } diff --git a/Microsoft.Azure.Cosmos/src/Resource/QueryResponses/QueryResponse.cs b/Microsoft.Azure.Cosmos/src/Resource/QueryResponses/QueryResponse.cs index cd761d3684..66f5ae320e 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/QueryResponses/QueryResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/QueryResponses/QueryResponse.cs @@ -28,6 +28,7 @@ private QueryResponse( int count, long responseLengthBytes, CosmosQueryResponseMessageHeaders responseHeaders, + IReadOnlyDictionary queryMetrics, HttpStatusCode statusCode, RequestMessage requestMessage, string errorMessage, @@ -42,6 +43,7 @@ private QueryResponse( this.CosmosElements = result; this.Count = count; this.ResponseLengthBytes = responseLengthBytes; + this.queryMetrics = queryMetrics; } public int Count { get; } @@ -72,6 +74,8 @@ private QueryResponse( /// internal ClientSideRequestStatistics RequestStatistics { get; } + internal IReadOnlyDictionary queryMetrics { get; set; } + internal virtual CosmosSerializationFormatOptions CosmosSerializationOptions { get; set; } internal bool GetHasMoreResults() @@ -83,7 +87,8 @@ internal static QueryResponse CreateSuccess( IEnumerable result, int count, long responseLengthBytes, - CosmosQueryResponseMessageHeaders responseHeaders) + CosmosQueryResponseMessageHeaders responseHeaders, + IReadOnlyDictionary queryMetrics = null) { if (count < 0) { @@ -100,6 +105,7 @@ internal static QueryResponse CreateSuccess( count: count, responseLengthBytes: responseLengthBytes, responseHeaders: responseHeaders, + queryMetrics: queryMetrics, statusCode: HttpStatusCode.OK, errorMessage: null, error: null, @@ -120,6 +126,7 @@ internal static QueryResponse CreateFailure( count: 0, responseLengthBytes: 0, responseHeaders: responseHeaders, + queryMetrics: null, statusCode: statusCode, errorMessage: errorMessage, error: error, @@ -144,11 +151,13 @@ private QueryResponse( HttpStatusCode httpStatusCode, IEnumerable cosmosElements, CosmosQueryResponseMessageHeaders responseMessageHeaders, + CosmosDiagnostics diagnostics, CosmosSerializer jsonSerializer, CosmosSerializationFormatOptions serializationOptions) { this.cosmosElements = cosmosElements; this.QueryHeaders = responseMessageHeaders; + this.Diagnostics = diagnostics; this.jsonSerializer = jsonSerializer; this.serializationOptions = serializationOptions; this.StatusCode = httpStatusCode; @@ -189,21 +198,6 @@ public override IEnumerable Resource } } - internal static QueryResponse CreateResponse( - ResponseMessage responseMessage, - CosmosSerializer jsonSerializer) - { - QueryResponse queryResponse = responseMessage as QueryResponse; - if (queryResponse == null) - { - throw new ArgumentException($"{nameof(responseMessage)} must be of type query response."); - } - - return QueryResponse.CreateResponse( - queryResponse, - jsonSerializer); - } - internal static QueryResponse CreateResponse( QueryResponse cosmosQueryResponse, CosmosSerializer jsonSerializer) @@ -216,10 +210,10 @@ internal static QueryResponse CreateResponse( httpStatusCode: cosmosQueryResponse.StatusCode, cosmosElements: cosmosQueryResponse.CosmosElements, responseMessageHeaders: cosmosQueryResponse.QueryHeaders, + diagnostics: cosmosQueryResponse.Diagnostics, jsonSerializer: jsonSerializer, serializationOptions: cosmosQueryResponse.CosmosSerializationOptions); } - return queryResponse; } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Response.cs b/Microsoft.Azure.Cosmos/src/Resource/Response.cs index 7962559709..d0959b01d0 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Response.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Response.cs @@ -63,6 +63,11 @@ public static implicit operator T(Response response) /// public abstract string ETag { get; } + /// + /// Gets the cosmos diagnostics information for the current request to Azure Cosmos DB service + /// + public CosmosDiagnostics Diagnostics { get; set; } + /// /// Gets the maximum size limit for this entity from the Azure Cosmos DB service. /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosClientSideRequestStatistics.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosClientSideRequestStatistics.cs new file mode 100644 index 0000000000..28845c463c --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosClientSideRequestStatistics.cs @@ -0,0 +1,184 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Text; + using Microsoft.Azure.Documents; + using Newtonsoft.Json; + + internal sealed class CosmosClientSideRequestStatistics : IClientSideRequestStatistics + { + internal const int MaxSupplementalRequestsForToString = 10; + + internal DateTime requestStartTime; + internal DateTime requestEndTime; + + private object lockObject = new object(); + + internal List responseStatisticsList; + internal List supplementalResponseStatisticsList; + internal Dictionary addressResolutionStatistics; + + [JsonIgnoreAttribute] + public List ContactedReplicas { get; set; } + [JsonIgnoreAttribute] + public HashSet FailedReplicas { get; private set; } + [JsonIgnoreAttribute] + public HashSet RegionsContacted { get; private set; } + + public CosmosClientSideRequestStatistics() + { + this.requestStartTime = DateTime.UtcNow; + this.requestEndTime = DateTime.UtcNow; + this.responseStatisticsList = new List(); + this.supplementalResponseStatisticsList = new List(); + this.addressResolutionStatistics = new Dictionary(); + this.ContactedReplicas = new List(); + this.FailedReplicas = new HashSet(); + this.RegionsContacted = new HashSet(); + } + + public TimeSpan RequestLatency + { + get + { + return this.requestEndTime - this.requestStartTime; + } + } + + public bool IsCpuOverloaded + { + get + { + foreach (StoreResponseStatistics responseStatistics in this.responseStatisticsList) + { + if (responseStatistics.StoreResult.IsClientCpuOverloaded) + { + return true; + } + } + foreach (StoreResponseStatistics responseStatistics in this.supplementalResponseStatisticsList) + { + if (responseStatistics.StoreResult.IsClientCpuOverloaded) + { + return true; + } + } + return false; + } + } + + public void RecordResponse(DocumentServiceRequest request, StoreResult storeResult) + { + DateTime responseTime = DateTime.UtcNow; + + StoreResponseStatistics responseStatistics; + responseStatistics.RequestResponseTime = responseTime; + responseStatistics.StoreResult = storeResult; + responseStatistics.RequestOperationType = request.OperationType; + responseStatistics.RequestResourceType = request.ResourceType; + + Uri locationEndpoint = request.RequestContext.LocationEndpointToRoute; + + lock (this.lockObject) + { + if (responseTime > this.requestEndTime) + { + this.requestEndTime = responseTime; + } + + if (locationEndpoint != null) + { + this.RegionsContacted.Add(locationEndpoint); + } + + if (responseStatistics.RequestOperationType == OperationType.Head || responseStatistics.RequestOperationType == OperationType.HeadFeed) + { + this.supplementalResponseStatisticsList.Add(responseStatistics); + } + else + { + this.responseStatisticsList.Add(responseStatistics); + } + } + } + + public string RecordAddressResolutionStart(Uri targetEndpoint) + { + string identifier = Guid.NewGuid().ToString(); + AddressResolutionStatistics resolutionStats = new AddressResolutionStatistics + { + StartTime = DateTime.UtcNow, + EndTime = DateTime.MaxValue, + TargetEndpoint = targetEndpoint == null ? "" : targetEndpoint.ToString() + }; + + lock (this.lockObject) + { + this.addressResolutionStatistics.Add(identifier, resolutionStats); + } + + return identifier; + } + + public void RecordAddressResolutionEnd(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + { + return; + } + + DateTime responseTime = DateTime.UtcNow; + lock (this.lockObject) + { + if (!this.addressResolutionStatistics.ContainsKey(identifier)) + { + throw new ArgumentException("Identifier {0} does not exist. Please call start before calling end.", identifier); + } + + if (responseTime > this.requestEndTime) + { + this.requestEndTime = responseTime; + } + + this.addressResolutionStatistics[identifier].EndTime = responseTime; + } + } + + internal struct StoreResponseStatistics + { + public DateTime RequestResponseTime; + public StoreResult StoreResult; + public ResourceType RequestResourceType; + public OperationType RequestOperationType; + + public override string ToString() + { + return String.Format(CultureInfo.InvariantCulture, "ResponseTime: {0}, StoreResult: {1}, ResourceType: {2}, OperationType: {3}", + this.RequestResponseTime.ToString("o", System.Globalization.CultureInfo.InvariantCulture), + this.StoreResult != null ? this.StoreResult.ToString() : string.Empty, + this.RequestResourceType, this.RequestOperationType); + } + } + + internal class AddressResolutionStatistics + { + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public string TargetEndpoint { get; set; } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "AddressResolution - StartTime: {0}, EndTime: {1}, TargetEndpoint: {2}", + this.StartTime.ToString("o", System.Globalization.CultureInfo.InvariantCulture), + this.EndTime.ToString("o", System.Globalization.CultureInfo.InvariantCulture), + this.TargetEndpoint); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosDiagnostics.cs new file mode 100644 index 0000000000..09aadd5a66 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosDiagnostics.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + /// + /// Contains the cosmos diagnostic information for the current request to Azure Cosmos DB service. + /// + public abstract class CosmosDiagnostics + { + /// + /// Gets the string field instance in the Azure CosmosDB database service. + /// + /// The string field instance in the Azure CosmosDB database service. + public abstract override string ToString(); + } +} diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/PointOperationStatistics.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/PointOperationStatistics.cs new file mode 100644 index 0000000000..d2c7c3ca23 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/PointOperationStatistics.cs @@ -0,0 +1,59 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.CosmosClientSideRequestStatistics; + + internal class PointOperationStatistics : CosmosDiagnostics + { + public DateTime requestStartTime { get; private set; } + + public DateTime requestEndTime { get; private set; } + + public List responseStatisticsList { get; private set; } + + public List supplementalResponseStatisticsList { get; private set; } + + public Dictionary addressResolutionStatistics { get; private set; } + + internal List contactedReplicas { get; set; } + + internal HashSet failedReplicas { get; private set; } + + public HashSet regionsContacted { get; private set; } + + public TimeSpan requestLatency { get; private set; } + + public PointOperationStatistics(CosmosClientSideRequestStatistics clientSideRequestStatistics) + { + this.requestStartTime = clientSideRequestStatistics.requestStartTime; + this.requestEndTime = clientSideRequestStatistics.requestEndTime; + this.responseStatisticsList = clientSideRequestStatistics.responseStatisticsList; + this.supplementalResponseStatisticsList = clientSideRequestStatistics.supplementalResponseStatisticsList; + this.addressResolutionStatistics = clientSideRequestStatistics.addressResolutionStatistics; + this.contactedReplicas = clientSideRequestStatistics.ContactedReplicas; + this.failedReplicas = clientSideRequestStatistics.FailedReplicas; + this.regionsContacted = clientSideRequestStatistics.RegionsContacted; + this.requestLatency = clientSideRequestStatistics.RequestLatency; + } + + public override string ToString() + { + if (this.supplementalResponseStatisticsList != null) + { + int supplementalResponseStatisticsListCount = this.supplementalResponseStatisticsList.Count; + int countToRemove = Math.Max(supplementalResponseStatisticsListCount - CosmosClientSideRequestStatistics.MaxSupplementalRequestsForToString, 0); + if (countToRemove > 0) + { + this.supplementalResponseStatisticsList.RemoveRange(0, countToRemove); + } + } + return JsonConvert.SerializeObject(this); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/QueryOperationStatistics.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/QueryOperationStatistics.cs new file mode 100644 index 0000000000..d8115a031e --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/QueryOperationStatistics.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System.Collections.Generic; + using Newtonsoft.Json; + + internal sealed class QueryOperationStatistics : CosmosDiagnostics + { + public QueryOperationStatistics(IReadOnlyDictionary queryMetrics = null) + { + this.queryMetrics = queryMetrics; + } + + public IReadOnlyDictionary queryMetrics { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/StoredProcedureResponse.cs b/Microsoft.Azure.Cosmos/src/StoredProcedureResponse.cs index 33d8dfb17a..be67e8e9fa 100644 --- a/Microsoft.Azure.Cosmos/src/StoredProcedureResponse.cs +++ b/Microsoft.Azure.Cosmos/src/StoredProcedureResponse.cs @@ -218,7 +218,7 @@ public TValue Response /// Gets the clientside request statics for execution of stored procedure. /// /// The clientside request statics for execution of stored procedure. - internal ClientSideRequestStatistics RequestStatistics + internal IClientSideRequestStatistics RequestStatistics { get { diff --git a/Microsoft.Azure.Cosmos/src/Util/Extensions.cs b/Microsoft.Azure.Cosmos/src/Util/Extensions.cs index 86b840d3d5..31fecd8eea 100644 --- a/Microsoft.Azure.Cosmos/src/Util/Extensions.cs +++ b/Microsoft.Azure.Cosmos/src/Util/Extensions.cs @@ -36,6 +36,15 @@ internal static ResponseMessage ToCosmosResponseMessage(this DocumentServiceResp } } + if (response.RequestStats != null) + { + CosmosClientSideRequestStatistics cosmosClientSideRequestStatistics = response.RequestStats as CosmosClientSideRequestStatistics; + if (cosmosClientSideRequestStatistics != null) + { + cosmosResponse.Diagnostics = new PointOperationStatistics(cosmosClientSideRequestStatistics); + } + } + return cosmosResponse; } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs new file mode 100644 index 0000000000..74af580587 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDiagnosticsTests.cs @@ -0,0 +1,152 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + [TestClass] + public class CosmosDiagnosticsTests : BaseCosmosClientHelper + { + private Container Container = null; + private ContainerProperties containerSettings = null; + + [TestInitialize] + public async Task TestInitialize() + { + await base.TestInit(); + string PartitionKey = "/status"; + this.containerSettings = new ContainerProperties(id: Guid.NewGuid().ToString(), partitionKeyPath: PartitionKey); + ContainerResponse response = await this.database.CreateContainerAsync( + this.containerSettings, + cancellationToken: this.cancellationToken); + Assert.IsNotNull(response); + Assert.IsNotNull(response.Container); + Assert.IsNotNull(response.Resource); + this.Container = response; + } + + [TestCleanup] + public async Task Cleanup() + { + await base.TestCleanup(); + } + + [TestMethod] + public async Task PointOperationDiagnostic() + { + //Checking point operation diagnostics on typed operations + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); + ItemResponse createResponse = await this.Container.CreateItemAsync(item: testItem); + Assert.IsNotNull(createResponse.Diagnostics); + + ItemResponse readResponse = await this.Container.ReadItemAsync(id: testItem.id, partitionKey: new PartitionKey(testItem.status)); + Assert.IsNotNull(readResponse.Diagnostics); + + testItem.description = "NewDescription"; + ItemResponse replaceResponse = await this.Container.ReplaceItemAsync(item: testItem, id: testItem.id, partitionKey: new PartitionKey(testItem.status)); + Assert.AreEqual(replaceResponse.Resource.description, "NewDescription"); + Assert.IsNotNull(replaceResponse.Diagnostics); + + ItemResponse deleteResponse = await this.Container.DeleteItemAsync(partitionKey: new Cosmos.PartitionKey(testItem.status), id: testItem.id); + Assert.IsNotNull(deleteResponse); + Assert.IsNotNull(deleteResponse.Diagnostics); + + //Checking point operation diagnostics on stream operations + ResponseMessage createStreamResponse = await this.Container.CreateItemStreamAsync( + partitionKey: new PartitionKey(testItem.status), + streamPayload: TestCommon.Serializer.ToStream(testItem)); + Assert.IsNotNull(createStreamResponse.Diagnostics); + + ResponseMessage readStreamResponse = await this.Container.ReadItemStreamAsync( + id: testItem.id, + partitionKey: new PartitionKey(testItem.status)); + Assert.IsNotNull(readStreamResponse.Diagnostics); + + ResponseMessage replaceStreamResponse = await this.Container.ReplaceItemStreamAsync( + streamPayload: TestCommon.Serializer.ToStream(testItem), + id: testItem.id, + partitionKey: new PartitionKey(testItem.status)); + Assert.IsNotNull(replaceStreamResponse.Diagnostics); + + ResponseMessage deleteStreamResponse = await this.Container.DeleteItemStreamAsync( + id: testItem.id, + partitionKey: new PartitionKey(testItem.status)); + Assert.IsNotNull(deleteStreamResponse.Diagnostics); + } + + [TestMethod] + public async Task QueryOperationDiagnostic() + { + IList itemList = await ToDoActivity.CreateRandomItems(this.Container, 3, randomPartitionKey: true); + + //Checking query metrics on typed query + ToDoActivity find = itemList.First(); + QueryDefinition sql = new QueryDefinition("select * from ToDoActivity"); + + QueryRequestOptions requestOptions = new QueryRequestOptions() + { + MaxItemCount = 1, + MaxConcurrency = 1, + }; + + FeedIterator feedIterator = this.Container.GetItemQueryIterator( + sql, + requestOptions: requestOptions); + + if (feedIterator.HasMoreResults) + { + FeedResponse iter = await feedIterator.ReadNextAsync(); + Assert.IsTrue(((QueryOperationStatistics)iter.Diagnostics).queryMetrics.Values.First().OutputDocumentCount > 0); + } + + sql = new QueryDefinition("select * from ToDoActivity t ORDER BY t.cost"); + feedIterator = this.Container.GetItemQueryIterator( + sql, + requestOptions: requestOptions); + if (feedIterator.HasMoreResults) + { + FeedResponse iter = await feedIterator.ReadNextAsync(); + Assert.IsTrue(((QueryOperationStatistics)iter.Diagnostics).queryMetrics.Values.First().OutputDocumentCount > 0); + } + + sql = new QueryDefinition("select DISTINCT t.cost from ToDoActivity t"); + feedIterator = this.Container.GetItemQueryIterator( + sql, + requestOptions: requestOptions); + if (feedIterator.HasMoreResults) + { + FeedResponse iter = await feedIterator.ReadNextAsync(); + Assert.IsNotNull((QueryOperationStatistics)iter.Diagnostics); + Assert.AreEqual(1, ((QueryOperationStatistics)iter.Diagnostics).queryMetrics.Values.First().OutputDocumentCount); + } + + sql = new QueryDefinition("select * from ToDoActivity OFFSET 1 LIMIT 1"); + feedIterator = this.Container.GetItemQueryIterator( + sql, + requestOptions: requestOptions); + if (feedIterator.HasMoreResults) + { + FeedResponse iter = await feedIterator.ReadNextAsync(); + Assert.IsTrue(((QueryOperationStatistics)iter.Diagnostics).queryMetrics.Values.First().OutputDocumentCount > 0); + } + + //Checking query metrics on stream query + sql = new QueryDefinition("select * from ToDoActivity"); + + FeedIterator iterator = this.Container.GetItemQueryStreamIterator( + sql, + requestOptions: requestOptions); + if (iterator.HasMoreResults) + { + ResponseMessage responseMessage = await iterator.ReadNextAsync(); + Assert.IsTrue(((QueryOperationStatistics)responseMessage.Diagnostics).queryMetrics.Values.First().OutputDocumentCount > 0); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj index 315be4553a..9c947bc5cd 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Microsoft.Azure.Cosmos.EmulatorTests.csproj @@ -8,7 +8,7 @@ false Microsoft.Azure.Cosmos Microsoft.Azure.Cosmos.EmulatorTests - 3.1.5 + 3.1.6 1.0.0-preview Microsoft.Azure.Cosmos.Direct Microsoft.Azure.Cosmos.Direct.MyGet diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Microsoft.Azure.Cosmos.Performance.Tests.csproj b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Microsoft.Azure.Cosmos.Performance.Tests.csproj index 12498ae823..610d9cb90d 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Microsoft.Azure.Cosmos.Performance.Tests.csproj +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Microsoft.Azure.Cosmos.Performance.Tests.csproj @@ -6,7 +6,7 @@ false false Microsoft.Azure.Cosmos - 3.1.3 + 3.1.6 1.0.0-preview Microsoft.Azure.Cosmos.Direct Microsoft.Azure.Cosmos.Direct.MyGet diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json index 1d2a57261d..e0f6712e17 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/DotNetSDKAPI.json @@ -1479,6 +1479,17 @@ }, "NestedTypes": {} }, + "CosmosDiagnostics": { + "Subclasses": {}, + "Members": { + "System.String ToString()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String ToString()" + } + }, + "NestedTypes": {} + }, "CosmosException": { "Subclasses": {}, "Members": { @@ -4583,6 +4594,18 @@ "Attributes": [], "MethodInfo": null }, + "Microsoft.Azure.Cosmos.CosmosDiagnostics Diagnostics": { + "Type": "Property", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics()" + }, "Microsoft.Azure.Cosmos.Headers get_Headers()": { "Type": "Method", "Attributes": [], @@ -4637,6 +4660,13 @@ "Type": "Property", "Attributes": [], "MethodInfo": null + }, + "Void set_Diagnostics(Microsoft.Azure.Cosmos.CosmosDiagnostics)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Diagnostics(Microsoft.Azure.Cosmos.CosmosDiagnostics)" } }, "NestedTypes": {} @@ -4654,6 +4684,18 @@ "Attributes": [], "MethodInfo": null }, + "Microsoft.Azure.Cosmos.CosmosDiagnostics Diagnostics": { + "Type": "Property", + "Attributes": [], + "MethodInfo": null + }, + "Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics()" + }, "Microsoft.Azure.Cosmos.Headers get_Headers()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -4746,6 +4788,13 @@ "Type": "Method", "Attributes": [], "MethodInfo": "Void set_Content(System.IO.Stream)" + }, + "Void set_Diagnostics(Microsoft.Azure.Cosmos.CosmosDiagnostics)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_Diagnostics(Microsoft.Azure.Cosmos.CosmosDiagnostics)" } }, "NestedTypes": {} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj index 48a9cb1b4e..5e2ceebd69 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj @@ -9,7 +9,7 @@ false false Microsoft.Azure.Cosmos - 3.1.5 + 3.1.6 1.0.0-preview Microsoft.Azure.Cosmos.Direct Microsoft.Azure.Cosmos.Direct.MyGet diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PointOperationStatisticsTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PointOperationStatisticsTest.cs new file mode 100644 index 0000000000..3c4422abb7 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PointOperationStatisticsTest.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using static Microsoft.Azure.Cosmos.CosmosClientSideRequestStatistics; + + [TestClass] + public class PointOperationStatisticsTest + { + [TestMethod] + public void ToStringTest() + { + + CosmosClientSideRequestStatistics cosmosClientSideRequestStatistics = new CosmosClientSideRequestStatistics(); + //Setting null supplementalResponseStatisticsList + cosmosClientSideRequestStatistics.supplementalResponseStatisticsList = null; + PointOperationStatistics pointOperationStatistics = new PointOperationStatistics(cosmosClientSideRequestStatistics); + pointOperationStatistics.ToString(); + Assert.IsNull(pointOperationStatistics.supplementalResponseStatisticsList); + + //Adding 5 objects supplementalResponseStatisticsList + cosmosClientSideRequestStatistics.supplementalResponseStatisticsList = new List + { + new StoreResponseStatistics(), + new StoreResponseStatistics(), + new StoreResponseStatistics(), + new StoreResponseStatistics(), + new StoreResponseStatistics() + }; + + pointOperationStatistics = new PointOperationStatistics(cosmosClientSideRequestStatistics); + pointOperationStatistics.ToString(); + Assert.AreEqual(5, pointOperationStatistics.supplementalResponseStatisticsList.Count); + + //Adding 5 more objects supplementalResponseStatisticsList, making total 10 + cosmosClientSideRequestStatistics.supplementalResponseStatisticsList.AddRange(new List() + { + new StoreResponseStatistics(), + new StoreResponseStatistics(), + new StoreResponseStatistics(), + new StoreResponseStatistics(), + new StoreResponseStatistics() + }); + + pointOperationStatistics = new PointOperationStatistics(cosmosClientSideRequestStatistics); + pointOperationStatistics.ToString(); + Assert.AreEqual(10, pointOperationStatistics.supplementalResponseStatisticsList.Count); + + //Adding 2 more objects supplementalResponseStatisticsList, making total 12 + cosmosClientSideRequestStatistics.supplementalResponseStatisticsList.AddRange(new List() + { + new StoreResponseStatistics(), + new StoreResponseStatistics() + }); + + pointOperationStatistics = new PointOperationStatistics(cosmosClientSideRequestStatistics); + pointOperationStatistics.ToString(); + Assert.AreEqual(10, pointOperationStatistics.supplementalResponseStatisticsList.Count); + } + } +} diff --git a/changelog.md b/changelog.md index ab890a132c..0e026964f1 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - [#100](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/100) Configurable Tcp settings to CosmosClientOptions +- [#615](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/615) Adding basic request diagnostics for V3 - [#729](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/729) Adding aggregate(CountAsync/SumAsync etc.) extensions for LINQ query - [#622](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/622) Added CRUD and query operations for Users and Permissions which enables [ResourceToken](https://docs.microsoft.com/en-us/azure/cosmos-db/secure-access-to-data#resource-tokens) support - [#716](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/716) Adding camel case serialization on LINQ query generation