From 4eb8e203d3d4a57fa9c8dc6598b4a4d0e4d67e27 Mon Sep 17 00:00:00 2001 From: leminh98 Date: Fri, 10 Sep 2021 12:44:36 -0700 Subject: [PATCH] Query: Adds PopulateIndexMetrics request options (#2612) This PR adds the option for customer to enable PopulateIndexMetrics in query request options. This allows customer to obtain the index utilization of their query. The information is aggregated and automatically showed up in QueryMetrics once this request option is enabled. Please note that enabling this incurs overhead, so please set it only during debugging. It also adds a field called IndexMetrics to FeedResponse to expose the result to users. --- .../src/Custom/DecryptableFeedResponse.cs | 3 + .../ChangeFeedEstimatorIterator.cs | 4 + .../Headers/CosmosMessageHeadersInternal.cs | 6 + .../CosmosQueryResponseMessageHeaders.cs | 6 +- Microsoft.Azure.Cosmos/src/Headers/Headers.cs | 6 + .../Query/Core/Metrics/IndexMetricWriter.cs | 133 ++++++++++++++++++ .../src/Query/v3Query/FeedResponse.cs | 14 +- .../src/Query/v3Query/QueryResponse.cs | 15 ++ .../src/Query/v3Query/ReadFeedResponse.cs | 2 + .../src/RequestOptions/QueryRequestOptions.cs | 18 +++ .../Query/PopulateIndexMetricsTest.cs | 79 +++++++++++ .../QueryTests.cs | 48 ------- .../Contracts/DotNetSDKAPI.json | 48 +++++++ 13 files changed, 330 insertions(+), 52 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricWriter.cs create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PopulateIndexMetricsTest.cs diff --git a/Microsoft.Azure.Cosmos.Encryption/src/Custom/DecryptableFeedResponse.cs b/Microsoft.Azure.Cosmos.Encryption/src/Custom/DecryptableFeedResponse.cs index ff18aaaa49..c54a933e43 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/Custom/DecryptableFeedResponse.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/Custom/DecryptableFeedResponse.cs @@ -33,6 +33,9 @@ protected DecryptableFeedResponse( public override CosmosDiagnostics Diagnostics { get; } +#if SDKPROJECTREF + public override string IndexMetrics => null; +#endif public override IEnumerator GetEnumerator() { return this.Resource.GetEnumerator(); diff --git a/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs b/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs index c1986d2518..a0af23801a 100644 --- a/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs +++ b/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs @@ -372,6 +372,8 @@ public ChangeFeedEstimatorFeedResponse( public override CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace); + public override string IndexMetrics => null; + public override IEnumerator GetEnumerator() { return this.remainingLeaseWorks.GetEnumerator(); @@ -403,6 +405,8 @@ public ChangeFeedEstimatorEmptyFeedResponse(ITrace trace) public override CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace); + public override string IndexMetrics => string.Empty; + public override IEnumerator GetEnumerator() { return ChangeFeedEstimatorEmptyFeedResponse.remainingLeaseWorks.GetEnumerator(); diff --git a/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs b/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs index f25144e842..4a793d258f 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs @@ -87,6 +87,12 @@ public virtual string QueryMetrics set => this.SetProperty(HttpConstants.HttpHeaders.QueryMetrics, value); } + public virtual string IndexUtilization + { + get => this.GetValueOrDefault(HttpConstants.HttpHeaders.IndexUtilization); + set => this.SetProperty(HttpConstants.HttpHeaders.IndexUtilization, value); + } + public virtual string BackendRequestDurationMilliseconds { get => this.GetValueOrDefault(HttpConstants.HttpHeaders.BackendRequestDurationMilliseconds); diff --git a/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs b/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs index 9324474f53..4c962cb6d8 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/CosmosQueryResponseMessageHeaders.cs @@ -72,7 +72,8 @@ internal CosmosQueryResponseMessageHeaders CloneKnownProperties( RetryAfterLiteral = this.RetryAfterLiteral, SubStatusCodeLiteral = this.SubStatusCodeLiteral, ContentType = this.ContentType, - QueryMetricsText = QueryMetricsText + QueryMetricsText = QueryMetricsText, + IndexUtilizationText = IndexUtilizationText }; } @@ -106,7 +107,8 @@ internal static CosmosQueryResponseMessageHeaders ConvertToQueryHeaders( RetryAfterLiteral = sourceHeaders.RetryAfterLiteral, SubStatusCodeLiteral = sourceHeaders.SubStatusCodeLiteral ?? (substatusCode.HasValue ? substatusCode.Value.ToString() : null), ContentType = sourceHeaders.ContentType, - QueryMetricsText = sourceHeaders.QueryMetricsText + QueryMetricsText = sourceHeaders.QueryMetricsText, + IndexUtilizationText = sourceHeaders.IndexUtilizationText }; } } diff --git a/Microsoft.Azure.Cosmos/src/Headers/Headers.cs b/Microsoft.Azure.Cosmos/src/Headers/Headers.cs index 4691e31a4c..29c65bef98 100644 --- a/Microsoft.Azure.Cosmos/src/Headers/Headers.cs +++ b/Microsoft.Azure.Cosmos/src/Headers/Headers.cs @@ -195,6 +195,12 @@ internal virtual string QueryMetricsText set => this.CosmosMessageHeaders.QueryMetrics = value; } + internal virtual string IndexUtilizationText + { + get => this.CosmosMessageHeaders.IndexUtilization; + set => this.CosmosMessageHeaders.IndexUtilization = value; + } + internal virtual string BackendRequestDurationMilliseconds { get => this.CosmosMessageHeaders.BackendRequestDurationMilliseconds; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricWriter.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricWriter.cs new file mode 100644 index 0000000000..7deb1f767b --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Metrics/IndexMetricWriter.cs @@ -0,0 +1,133 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Query.Core.Metrics +{ + using System; + using System.Globalization; + using System.Linq; + using System.Text; + + /// + /// Base class for visiting and serializing a . + /// +#if INTERNAL +#pragma warning disable SA1600 +#pragma warning disable CS1591 + public +#else + internal +#endif + class IndexMetricWriter + { + private const string IndexUtilizationInfo = "Index Utilization Information"; + private const string UtilizedSingleIndexes = "Utilized Single Indexes"; + private const string PotentialSingleIndexes = "Potential Single Indexes"; + private const string UtilizedCompositeIndexes = "Utilized Composite Indexes"; + private const string PotentialCompositeIndexes = "Potential Composite Indexes"; + private const string IndexExpression = "Index Spec"; + private const string IndexImpactScore = "Index Impact Score"; + + private const string IndexUtilizationSeparator = "---"; + + private readonly StringBuilder stringBuilder; + + public IndexMetricWriter(StringBuilder stringBuilder) + { + this.stringBuilder = stringBuilder ?? throw new ArgumentNullException($"{nameof(stringBuilder)} must not be null."); + } + + public void WriteIndexMetrics(IndexUtilizationInfo indexUtilizationInfo) + { + // IndexUtilizationInfo + this.WriteBeforeIndexUtilizationInfo(); + + this.WriteIndexUtilizationInfo(indexUtilizationInfo); + + this.WriteAfterIndexUtilizationInfo(); + } + + #region IndexUtilizationInfo + protected void WriteBeforeIndexUtilizationInfo() + { + IndexMetricWriter.AppendNewlineToStringBuilder(this.stringBuilder); + IndexMetricWriter.AppendHeaderToStringBuilder( + this.stringBuilder, + IndexMetricWriter.IndexUtilizationInfo, + indentLevel: 0); + } + + protected void WriteIndexUtilizationInfo(IndexUtilizationInfo indexUtilizationInfo) + { + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.UtilizedSingleIndexes, indentLevel: 1); + + foreach (SingleIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.UtilizedSingleIndexes) + { + WriteSingleIndexUtilizationEntity(indexUtilizationEntity); + } + + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.PotentialSingleIndexes, indentLevel: 1); + + foreach (SingleIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.PotentialSingleIndexes) + { + WriteSingleIndexUtilizationEntity(indexUtilizationEntity); + } + + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.UtilizedCompositeIndexes, indentLevel: 1); + + foreach (CompositeIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.UtilizedCompositeIndexes) + { + WriteCompositeIndexUtilizationEntity(indexUtilizationEntity); + } + + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.PotentialCompositeIndexes, indentLevel: 1); + + foreach (CompositeIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.PotentialCompositeIndexes) + { + WriteCompositeIndexUtilizationEntity(indexUtilizationEntity); + } + + void WriteSingleIndexUtilizationEntity(SingleIndexUtilizationEntity indexUtilizationEntity) + { + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricWriter.IndexExpression}: {indexUtilizationEntity.IndexDocumentExpression}", indentLevel: 2); + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricWriter.IndexImpactScore}: {indexUtilizationEntity.IndexImpactScore}", indentLevel: 2); + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.IndexUtilizationSeparator, indentLevel: 2); + } + + void WriteCompositeIndexUtilizationEntity(CompositeIndexUtilizationEntity indexUtilizationEntity) + { + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricWriter.IndexExpression}: {String.Join(", ", indexUtilizationEntity.IndexDocumentExpressions)}", indentLevel: 2); + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricWriter.IndexImpactScore}: {indexUtilizationEntity.IndexImpactScore}", indentLevel: 2); + IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.IndexUtilizationSeparator, indentLevel: 2); + } + } + + protected void WriteAfterIndexUtilizationInfo() + { + // Do nothing + } + #endregion + + #region Helpers + private static void AppendHeaderToStringBuilder(StringBuilder stringBuilder, string headerTitle, int indentLevel) + { + const string Indent = " "; + const string FormatString = "{0}{1}"; + + stringBuilder.AppendFormat( + CultureInfo.InvariantCulture, + FormatString, + string.Concat(Enumerable.Repeat(Indent, indentLevel)) + headerTitle, + Environment.NewLine); + } + + private static void AppendNewlineToStringBuilder(StringBuilder stringBuilder) + { + IndexMetricWriter.AppendHeaderToStringBuilder( + stringBuilder, + string.Empty, + indentLevel: 0); + } + #endregion + } +} diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs index 200908d21e..f18d64da5e 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/FeedResponse.cs @@ -43,13 +43,23 @@ protected FeedResponse() public abstract int Count { get; } /// - /// Get an enumerator of the object + /// Gets the index utilization metrics to be used for debugging purposes. + /// It's applicable to query response only. Other feed response will return null for this field. + /// This result is only available if QueryRequestOptions.PopulateIndexMetrics is set to true. + /// + /// + /// The index utilization metrics. + /// + public abstract string IndexMetrics { get; } + + /// + /// Get an enumerator of the object. /// /// An instance of an Enumerator public abstract IEnumerator GetEnumerator(); /// - /// Get an enumerator of the object + /// Get an enumerator of the object. /// /// An instance of an Enumerator IEnumerator IEnumerable.GetEnumerator() diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs index 0ce3957cc9..077e0b1ae2 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryResponse.cs @@ -7,7 +7,9 @@ namespace Microsoft.Azure.Cosmos using System.Collections.Generic; using System.IO; using System.Net; + using System.Text; using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.Query.Core.Metrics; using Microsoft.Azure.Cosmos.Serializer; using Microsoft.Azure.Cosmos.Tracing; @@ -179,6 +181,15 @@ private QueryResponse( this.Resource = CosmosElementSerializer.GetResources( cosmosArray: cosmosElements, serializerCore: serializerCore); + + this.IndexUtilizationText = new Lazy(() => + { + IndexUtilizationInfo parsedIndexUtilizationInfo = IndexUtilizationInfo.CreateFromString(responseMessageHeaders.IndexUtilizationText); + StringBuilder stringBuilder = new StringBuilder(); + IndexMetricWriter indexMetricWriter = new IndexMetricWriter(stringBuilder); + indexMetricWriter.WriteIndexMetrics(parsedIndexUtilizationInfo); + return stringBuilder.ToString(); + }); } public override string ContinuationToken => this.Headers.ContinuationToken; @@ -195,6 +206,10 @@ private QueryResponse( internal CosmosQueryResponseMessageHeaders QueryHeaders { get; } + private Lazy IndexUtilizationText { get; } + + public override string IndexMetrics => this.IndexUtilizationText.Value; + public override IEnumerator GetEnumerator() { return this.Resource.GetEnumerator(); diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/ReadFeedResponse.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/ReadFeedResponse.cs index 59c00c8941..427a189668 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/ReadFeedResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/ReadFeedResponse.cs @@ -34,6 +34,8 @@ internal ReadFeedResponse( public override CosmosDiagnostics Diagnostics { get; } + public override string IndexMetrics { get; } + public override IEnumerator GetEnumerator() { return this.Resource.GetEnumerator(); diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs index 5a78eccc5e..ac44ee34d4 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs @@ -92,6 +92,19 @@ public class QueryRequestOptions : RequestOptions /// public PartitionKey? PartitionKey { get; set; } + /// + /// Gets or sets the request option for document query requests in the Azure Cosmos DB service. + /// + /// + /// + /// PopulateIndexMetrics is used to obtain the index metrics to understand how the query engine used existing indexes + /// and how it could use potential new indexes. + /// The results will be displayed in FeedResponse.IndexMetrics. Please note that this options will incur overhead, so it should be + /// enabled only when debugging slow queries. + /// + /// + public bool? PopulateIndexMetrics { get; set; } + /// /// Gets or sets the consistency level required for the request in the Azure Cosmos DB service. /// @@ -242,6 +255,11 @@ internal override void PopulateRequestOptions(RequestMessage request) request.Headers.Set(HttpConstants.HttpHeaders.EnumerationDirection, this.EnumerationDirection.Value.ToString()); } + if (this.PopulateIndexMetrics.HasValue) + { + request.Headers.Add(HttpConstants.HttpHeaders.PopulateIndexMetrics, this.PopulateIndexMetrics.ToString()); + } + DedicatedGatewayRequestOptions.PopulateMaxIntegratedCacheStalenessOption(this.DedicatedGatewayRequestOptions, request); request.Headers.Add(HttpConstants.HttpHeaders.PopulateQueryMetrics, bool.TrueString); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PopulateIndexMetricsTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PopulateIndexMetricsTest.cs new file mode 100644 index 0000000000..3191d97801 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PopulateIndexMetricsTest.cs @@ -0,0 +1,79 @@ +namespace Microsoft.Azure.Cosmos.EmulatorTests.Query +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.Query.Core.Metrics; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Microsoft.Azure.Documents; + using Newtonsoft.Json; + using Microsoft.Azure.Cosmos.SDK.EmulatorTests.QueryOracle; + + [TestClass] + public sealed class PopulateIndexMetricsTest : QueryTestsBase + { + [TestMethod] + public async Task TestIndexMetricsHeaderExistence() + { + int seed = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + uint numberOfDocuments = 1; + QueryOracleUtil util = new QueryOracle2(seed); + IEnumerable inputDocuments = util.GetDocuments(numberOfDocuments); + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct, + CollectionTypes.SinglePartition | CollectionTypes.MultiPartition, + inputDocuments, + ImplementationAsync, + "/id"); + + static async Task ImplementationAsync(Container container, IReadOnlyList documents) + { + string query = string.Format("SELECT * FROM c WHERE c.name = 'ABC' AND c.age > 12"); + + // Build the expected string + Assert.IsTrue(IndexUtilizationInfo.TryCreateFromDelimitedString("eyJVdGlsaXplZFNpbmdsZUluZGV4ZXMiOlt7IkZpbHRlckV4cHJlc3Npb24iOiIoUk9PVC5uYW1lID0gXCJBQkNcIikiLCJJbmRleFNwZWMiOiJcL25hbWVcLz8iLCJGaWx0ZXJQcmVjaXNlU2V0Ijp0cnVlLCJJbmRleFByZWNpc2VTZXQiOnRydWUsIkluZGV4SW1wYWN0U2NvcmUiOiJIaWdoIn0seyJGaWx0ZXJFeHByZXNzaW9uIjoiKFJPT1QuYWdlID4gMTIpIiwiSW5kZXhTcGVjIjoiXC9hZ2VcLz8iLCJGaWx0ZXJQcmVjaXNlU2V0Ijp0cnVlLCJJbmRleFByZWNpc2VTZXQiOnRydWUsIkluZGV4SW1wYWN0U2NvcmUiOiJIaWdoIn1dLCJQb3RlbnRpYWxTaW5nbGVJbmRleGVzIjpbXSwiVXRpbGl6ZWRDb21wb3NpdGVJbmRleGVzIjpbXSwiUG90ZW50aWFsQ29tcG9zaXRlSW5kZXhlcyI6W3siSW5kZXhTcGVjcyI6WyJcL25hbWUgQVNDIiwiXC9hZ2UgQVNDIl0sIkluZGV4UHJlY2lzZVNldCI6ZmFsc2UsIkluZGV4SW1wYWN0U2NvcmUiOiJIaWdoIn1dfQ==", + out IndexUtilizationInfo parsedInfo)); + StringBuilder stringBuilder = new StringBuilder(); + IndexMetricWriter indexMetricWriter = new IndexMetricWriter(stringBuilder); + indexMetricWriter.WriteIndexMetrics(parsedInfo); + string expectedIndexMetricsString = stringBuilder.ToString(); + + // Test using GetItemQueryIterator + QueryRequestOptions requestOptions = new QueryRequestOptions() { PopulateIndexMetrics = true }; + FeedIterator itemQuery = container.GetItemQueryIterator( + query, + requestOptions: requestOptions); + + while (itemQuery.HasMoreResults) + { + FeedResponse page = await itemQuery.ReadNextAsync(); + Assert.IsTrue(page.Headers.AllKeys().Length > 1); + Assert.IsNotNull(page.Headers.Get(HttpConstants.HttpHeaders.IndexUtilization), "Expected index utilization headers for query"); + Assert.IsNotNull(page.IndexMetrics, "Expected index metrics response for query"); + Assert.AreEqual(expectedIndexMetricsString, page.IndexMetrics); + } + + // Test using Stream API + using (FeedIterator feedIterator = container.GetItemQueryStreamIterator( + queryText: query, + continuationToken: null, + requestOptions: new QueryRequestOptions + { + PopulateIndexMetrics = true, + })) + { + using (ResponseMessage response = await feedIterator.ReadNextAsync()) + { + Assert.IsNotNull(response.Content); + Assert.IsTrue(response.Headers.AllKeys().Length > 1); + Assert.IsNotNull(response.Headers.Get(HttpConstants.HttpHeaders.IndexUtilization), "Expected index utilization headers for query"); + } + } + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryTests.cs index 1ab0d97fbe..9a69e88b90 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryTests.cs @@ -1824,54 +1824,6 @@ public void TestQueryMetricsHeaders() }); } - [Ignore] // Ignore until backend index utilization is on by default and other query metrics test are completed - [TestMethod] - public async Task TestIndexUtilizationParsing() - { - - Database database = await this.client.CreateDatabaseAsync(new Database() { Id = Guid.NewGuid().ToString() }); - - DocumentCollection collection; - RequestOptions options = new RequestOptions(); - - collection = new DocumentCollection() - { - Id = Guid.NewGuid().ToString() - }; - - options.OfferThroughput = 10000; - - collection = await TestCommon.CreateCollectionAsync(this.client, database, collection, options); - - int maxDocumentCount = 2000; - for (int i = 0; i < maxDocumentCount; i++) - { - QueryDocument doc = new QueryDocument() - { - Id = Guid.NewGuid().ToString(), - NumericField = i, - StringField = i.ToString(CultureInfo.InvariantCulture), - }; - - await this.client.CreateDocumentAsync(collection, doc); - } - - DocumentFeedResponse result = await this.client.CreateDocumentQuery(collection, "SELECT r.id FROM root r WHERE r.name = 'Julien' and r.age > 12", new FeedOptions() { PopulateQueryMetrics = true, EnableCrossPartitionQuery = true }).AsDocumentQuery().ExecuteNextAsync(); - Assert.IsNotNull(result.ResponseHeaders[WFConstants.BackendHeaders.QueryMetrics], "Expected metrics headers for query"); - Assert.IsNotNull(result.ResponseHeaders[WFConstants.BackendHeaders.IndexUtilization], "Expected index utilization headers for query"); - - QueryMetrics queryMetrics = new QueryMetrics( - BackendMetrics.ParseFromDelimitedString(result.ResponseHeaders[WFConstants.BackendHeaders.QueryMetrics]), - IndexUtilizationInfo.CreateFromString(result.ResponseHeaders[WFConstants.BackendHeaders.IndexUtilization]), - ClientSideMetrics.Empty); - - // If these fields populate then the parsing is successful and correct. - Assert.AreEqual("/name/?", queryMetrics.IndexUtilizationInfo.UtilizedSingleIndexes[0].IndexDocumentExpression); - Assert.AreEqual(String.Join(", ", new object[] { "/name ASC", "/age ASC" }), String.Join(", ", queryMetrics.IndexUtilizationInfo.PotentialCompositeIndexes[0].IndexDocumentExpressions)); - - await this.client.DeleteDatabaseAsync(database); - } - [TestMethod] public async Task TestQueryMetricsNonZero() { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json index 74efdb4307..cced145d53 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json @@ -3311,6 +3311,16 @@ "Type": "Method", "Attributes": [], "MethodInfo": "System.String get_ETag();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_IndexMetrics()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String get_IndexMetrics();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String IndexMetrics": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String IndexMetrics;CanRead:True;CanWrite:False;System.String get_IndexMetrics();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -5045,6 +5055,18 @@ ], "MethodInfo": "System.Nullable`1[System.Boolean] get_EnableScanInQuery();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "System.Nullable`1[System.Boolean] get_PopulateIndexMetrics()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Boolean] get_PopulateIndexMetrics();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Boolean] PopulateIndexMetrics": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.Boolean] PopulateIndexMetrics;CanRead:True;CanWrite:True;System.Nullable`1[System.Boolean] get_PopulateIndexMetrics();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "System.Nullable`1[System.Int32] get_MaxBufferedItemCount()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -5157,6 +5179,13 @@ ], "MethodInfo": "Void set_PartitionKey(System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_ResponseContinuationTokenLimitInKb(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -5919,6 +5948,18 @@ ], "MethodInfo": "System.Nullable`1[System.Boolean] get_EnableScanInQuery();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "System.Nullable`1[System.Boolean] get_PopulateIndexMetrics()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[System.Boolean] get_PopulateIndexMetrics();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.Boolean] PopulateIndexMetrics": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.Boolean] PopulateIndexMetrics;CanRead:True;CanWrite:True;System.Nullable`1[System.Boolean] get_PopulateIndexMetrics();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "System.Nullable`1[System.Int32] get_MaxBufferedItemCount()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -6031,6 +6072,13 @@ ], "MethodInfo": "Void set_PartitionKey(System.Nullable`1[Microsoft.Azure.Cosmos.PartitionKey]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_ResponseContinuationTokenLimitInKb(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [