Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Internal] Query: Adds Index Metrics V2 Object Model #4058

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1d90475
making necessary ownership change
Jun 6, 2022
33e63cf
made change to ownerships
Jun 16, 2022
22036cb
header test
Jul 13, 2022
bc6d13d
Call to TryCreate instead of Create in Responsemessage
Jul 21, 2022
67f6040
merge with master
Aug 24, 2022
d498b12
Add baseline test infra for index metric parser
Aug 25, 2022
5a6892f
update baseline files
Aug 25, 2022
a827284
Add parse retry logic
Aug 25, 2022
97f7b9d
Update headers test
Aug 25, 2022
70098c7
Merge branch 'master' into users/leminh/HeaderTest
leminh98 Aug 25, 2022
33f2d70
address code review
Sep 19, 2022
d3c7963
Merge branch 'users/leminh/HeaderTest' of https://github.com/Azure/az…
Sep 19, 2022
0acf34a
address code review
Sep 28, 2022
122ebc9
fix tests
Sep 28, 2022
1ff34c2
Merge branch 'master' into users/leminh/HeaderTest
leminh98 Sep 28, 2022
df203c9
Update csproj file
Sep 28, 2022
f533a2e
Merge branch 'users/leminh/HeaderTest' of https://github.com/Azure/az…
Sep 28, 2022
3c85ea9
Adopt the new header
Sep 28, 2022
3cb88f8
Merge branch 'master' into users/leminh/AdoptionOfPlainTextIndexAdvis…
Oct 10, 2022
ffe67e2
Merge branch 'master' into users/leminh/AdoptionOfPlainTextIndexAdvis…
Mar 28, 2023
6dfce3d
update the response to parse with text instead of base 64
Mar 28, 2023
e89aedd
test for headers adoption of uri escape
May 1, 2023
df1eb73
Add URI Decode logic
May 1, 2023
271519a
Update baseline
May 1, 2023
bc241a3
Merge branch 'master' into users/leminh/AdoptionOfPlainTextIndexAdvis…
Aug 16, 2023
232e4b0
Update with the new header name from back end
Aug 16, 2023
9431044
update the query parsing requirement
Aug 28, 2023
e7ada35
New Index Metrics DOM
Aug 29, 2023
5c75eb2
Merge branch 'master' into users/leminh/IndexAdvisorV2ObjectModel
leminh98 Aug 29, 2023
d0a8c43
fix build error
Aug 29, 2023
5c772f2
Code clean up
Aug 29, 2023
764d3ef
Address code review
Sep 11, 2023
8c11593
Merge remote-tracking branch 'origin/users/leminh/IndexAdvisorV2Objec…
Sep 11, 2023
c68a9d0
Turn off switching to V2
Sep 11, 2023
5c6e47b
Fix test
Sep 11, 2023
1ee19e2
Merge branch 'master' into users/leminh/IndexAdvisorV2ObjectModel
Sep 11, 2023
d327a84
fix test errors
Sep 11, 2023
87e6090
Address code review comment
Sep 14, 2023
29a60f1
addressed code review
Sep 15, 2023
931f0c5
removed the empty entity
Sep 18, 2023
38425b2
update test parse
Sep 18, 2023
973ee61
Merge branch 'master' into users/leminh/IndexAdvisorV2ObjectModel
leminh98 Sep 18, 2023
5a81143
update test
Sep 19, 2023
0ed70c7
Merge branch 'users/leminh/IndexAdvisorV2ObjectModel' of https://gith…
Sep 19, 2023
af43b9a
Merge branch 'master' into users/leminh/IndexAdvisorV2ObjectModel
leminh98 Sep 19, 2023
dc3c10e
Merge branch 'master' into users/leminh/IndexAdvisorV2ObjectModel
leminh98 Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,21 +251,27 @@ private void CheckDisposed()
/// Decode the Index Metrics from the response headers, if exists.
/// </summary>
/// <param name="responseMessageHeaders">The response headers</param>
/// <param name="isBse64Encoded">The encoding of the IndexMetrics response</param>
/// <param name="isBase64Encoded">The encoding of the IndexMetrics response</param>
/// <returns>Lazy implementation of the pretty-printed IndexMetrics</returns>
static internal Lazy<string> DecodeIndexMetrics(Headers responseMessageHeaders, bool isBse64Encoded)
static internal Lazy<string> DecodeIndexMetrics(Headers responseMessageHeaders, bool isBase64Encoded)
{
if (responseMessageHeaders?.IndexUtilizationText != null)
{
return new Lazy<string>(() =>
{
IndexUtilizationInfo parsedIndexUtilizationInfo = IndexUtilizationInfo.CreateFromString(responseMessageHeaders.IndexUtilizationText, isBse64Encoded);

StringBuilder stringBuilder = new StringBuilder();
IndexMetricWriter indexMetricWriter = new IndexMetricWriter(stringBuilder);
indexMetricWriter.WriteIndexMetrics(parsedIndexUtilizationInfo);
if (isBase64Encoded)
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
{
IndexUtilizationInfo parsedIndexUtilizationInfo = IndexUtilizationInfo.CreateFromString(responseMessageHeaders.IndexUtilizationText);

return stringBuilder.ToString();
StringBuilder stringBuilder = new StringBuilder();
IndexMetricsWriter indexMetricWriter = new IndexMetricsWriter(stringBuilder);
indexMetricWriter.WriteIndexMetrics(parsedIndexUtilizationInfo);

return stringBuilder.ToString();
}

// Return the JSON from the response header
return responseMessageHeaders.IndexUtilizationText;
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Query.Core.Metrics
{
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

/// <summary>
/// Query index utilization data for composite indexes (sub-structure of the Index Metrics class) in the Azure Cosmos database service.
/// </summary>
#if INTERNAL
#pragma warning disable SA1600
#pragma warning disable CS1591
public
#else
internal
#endif
sealed class CompositeIndexIndexMetrics
{
/// <summary>
/// Initialized a new instance of an Index Metrics' Composite Index class.
/// </summary>
/// <param name="indexDocumentExpressions">The string list representation of the composite index.</param>
/// <param name="indexImpactScore">The index impact score.</param>
[JsonConstructor]
private CompositeIndexIndexMetrics(
IReadOnlyList<string> indexDocumentExpressions,
string indexImpactScore)
{
this.IndexSpecs = indexDocumentExpressions;
this.IndexImpactScore = indexImpactScore;
}

/// <summary>
/// String list representation of index paths of a composite index.
/// </summary>
[JsonProperty(PropertyName = "IndexSpecs")]
public IReadOnlyList<string> IndexSpecs { get; }

/// <summary>
/// The index impact score of the composite index.
/// </summary>
[JsonProperty(PropertyName = "IndexImpactScore")]
public string IndexImpactScore { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Query.Core.Metrics
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Newtonsoft.Json;

/// <summary>
/// Query index utilization data for composite indexes (sub-structure of the Index Metrics class) in the Azure Cosmos database service.
/// </summary>
#if INTERNAL
#pragma warning disable SA1600
#pragma warning disable CS1591
public
#else
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
internal
#endif
sealed class IndexMetricsInfo
{
/// <summary>
/// Initializes a new instance of the Index Metrics class.
/// </summary>
/// <param name="utilizedEntity">The utilized indexes</param>
/// <param name="potentialEntity">The potential indexes</param>
[JsonConstructor]
public IndexMetricsInfo(
IndexMetricsInfoEntity utilizedEntity,
IndexMetricsInfoEntity potentialEntity)
{
this.UtilizedEntity = utilizedEntity;
this.PotentialEntity = potentialEntity;
}

[JsonProperty("Utilized")]
public IndexMetricsInfoEntity UtilizedEntity { get; }

[JsonProperty("Potential")]
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
public IndexMetricsInfoEntity PotentialEntity { get; }

/// <summary>
/// Creates a new IndexMetricsInfo from the backend delimited string.
/// </summary>
/// <param name="delimitedString">The backend delimited string to deserialize from.</param>
/// <param name="result">The parsed index utilization info</param>
/// <returns>A new IndexMetricsInfo from the backend delimited string.</returns>
public static bool TryCreateFromString(string delimitedString, out IndexMetricsInfo result)
{
if (delimitedString == null)
{
result = null;
return false;
}

try
{
// Decode and deserialize the response string
string decodedString = System.Web.HttpUtility.UrlDecode(delimitedString, Encoding.UTF8);

result = JsonConvert.DeserializeObject<IndexMetricsInfo>(decodedString, new JsonSerializerSettings()
{
// Allowing null values to be resilient to Json structure change
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
// Ignore parsing error encountered in deserialization
Error = (sender, parsingErrorEvent) => parsingErrorEvent.ErrorContext.Handled = true
}) ?? null;

return true;
}
catch (JsonException)
{
result = null;
return false;
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Query.Core.Metrics
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Newtonsoft.Json;

/// <summary>
/// Query index utilization metrics in the Azure Cosmos database service.
/// </summary>
#if INTERNAL
#pragma warning disable SA1600
#pragma warning disable CS1591
public
#else
internal
#endif
sealed class IndexMetricsInfoEntity
{
/// <summary>
/// Initializes a new instance of the Index Utilization class. This is the legacy class of IndexMetricsInfoEntity.
/// </summary>
/// <param name="singleIndexes">The utilized single indexes list</param>
/// <param name="compositeIndexes">The potential single indexes list</param>
[JsonConstructor]
public IndexMetricsInfoEntity(
IReadOnlyList<SingleIndexIndexMetrics> singleIndexes,
IReadOnlyList<CompositeIndexIndexMetrics> compositeIndexes)
{
this.SingleIndexes = (singleIndexes ?? Enumerable.Empty<SingleIndexIndexMetrics>()).Where(item => item != null).ToList();
this.CompositeIndexes = (compositeIndexes ?? Enumerable.Empty<CompositeIndexIndexMetrics>()).Where(item => item != null).ToList();
}

public IReadOnlyList<SingleIndexIndexMetrics> SingleIndexes { get; }
public IReadOnlyList<CompositeIndexIndexMetrics> CompositeIndexes { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics
using System.Text;

/// <summary>
/// Base class for visiting and serializing a <see cref="QueryMetrics"/>.
/// Base class for visiting and serializing a <see cref="IndexUtilizationInfo"/>.
/// </summary>
#if INTERNAL
#pragma warning disable SA1600
Expand All @@ -18,7 +18,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics
#else
internal
#endif
class IndexMetricWriter
class IndexMetricsWriter
{
private const string IndexUtilizationInfo = "Index Utilization Information";
private const string UtilizedSingleIndexes = "Utilized Single Indexes";
Expand All @@ -32,7 +32,7 @@ class IndexMetricWriter

private readonly StringBuilder stringBuilder;

public IndexMetricWriter(StringBuilder stringBuilder)
public IndexMetricsWriter(StringBuilder stringBuilder)
{
this.stringBuilder = stringBuilder ?? throw new ArgumentNullException($"{nameof(stringBuilder)} must not be null.");
}
Expand All @@ -50,37 +50,37 @@ public void WriteIndexMetrics(IndexUtilizationInfo indexUtilizationInfo)
#region IndexUtilizationInfo
protected void WriteBeforeIndexUtilizationInfo()
{
IndexMetricWriter.AppendNewlineToStringBuilder(this.stringBuilder);
IndexMetricWriter.AppendHeaderToStringBuilder(
IndexMetricsWriter.AppendNewlineToStringBuilder(this.stringBuilder);
IndexMetricsWriter.AppendHeaderToStringBuilder(
this.stringBuilder,
IndexMetricWriter.IndexUtilizationInfo,
IndexMetricsWriter.IndexUtilizationInfo,
indentLevel: 0);
}

protected void WriteIndexUtilizationInfo(IndexUtilizationInfo indexUtilizationInfo)
{
IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.UtilizedSingleIndexes, indentLevel: 1);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricsWriter.UtilizedSingleIndexes, indentLevel: 1);

foreach (SingleIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.UtilizedSingleIndexes)
{
WriteSingleIndexUtilizationEntity(indexUtilizationEntity);
}

IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.PotentialSingleIndexes, indentLevel: 1);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricsWriter.PotentialSingleIndexes, indentLevel: 1);

foreach (SingleIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.PotentialSingleIndexes)
{
WriteSingleIndexUtilizationEntity(indexUtilizationEntity);
}

IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.UtilizedCompositeIndexes, indentLevel: 1);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricsWriter.UtilizedCompositeIndexes, indentLevel: 1);

foreach (CompositeIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.UtilizedCompositeIndexes)
{
WriteCompositeIndexUtilizationEntity(indexUtilizationEntity);
}

IndexMetricWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricWriter.PotentialCompositeIndexes, indentLevel: 1);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricsWriter.PotentialCompositeIndexes, indentLevel: 1);

foreach (CompositeIndexUtilizationEntity indexUtilizationEntity in indexUtilizationInfo.PotentialCompositeIndexes)
{
Expand All @@ -89,16 +89,16 @@ protected void WriteIndexUtilizationInfo(IndexUtilizationInfo indexUtilizationIn

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);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricsWriter.IndexExpression}: {indexUtilizationEntity.IndexDocumentExpression}", indentLevel: 2);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricsWriter.IndexImpactScore}: {indexUtilizationEntity.IndexImpactScore}", indentLevel: 2);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricsWriter.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);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricsWriter.IndexExpression}: {String.Join(", ", indexUtilizationEntity.IndexDocumentExpressions)}", indentLevel: 2);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, $"{IndexMetricsWriter.IndexImpactScore}: {indexUtilizationEntity.IndexImpactScore}", indentLevel: 2);
IndexMetricsWriter.AppendHeaderToStringBuilder(this.stringBuilder, IndexMetricsWriter.IndexUtilizationSeparator, indentLevel: 2);
}
}

Expand All @@ -123,7 +123,7 @@ private static void AppendHeaderToStringBuilder(StringBuilder stringBuilder, str

private static void AppendNewlineToStringBuilder(StringBuilder stringBuilder)
{
IndexMetricWriter.AppendHeaderToStringBuilder(
IndexMetricsWriter.AppendHeaderToStringBuilder(
stringBuilder,
string.Empty,
indentLevel: 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Metrics
using System.Linq;
using System.Text;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Newtonsoft.Json;

/// <summary>
Expand All @@ -29,7 +30,7 @@ sealed class IndexUtilizationInfo
potentialCompositeIndexes: new List<CompositeIndexUtilizationEntity>());

/// <summary>
/// Initializes a new instance of the Index Utilization class.
/// Initializes a new instance of the Index Utilization class. This is the legacy class of IndexMetricsInfo.
/// </summary>
/// <param name="utilizedSingleIndexes">The utilized single indexes list</param>
/// <param name="potentialSingleIndexes">The potential single indexes list</param>
Expand Down Expand Up @@ -60,33 +61,22 @@ public IndexUtilizationInfo(
/// <param name="result">The parsed index utilization info</param>
/// <returns>A new IndexUtilizationInfo from the backend delimited string.</returns>
internal static bool TryCreateFromDelimitedBase64String(string delimitedString, out IndexUtilizationInfo result)
{
if (delimitedString == null)
{
result = IndexUtilizationInfo.Empty;
return true;
}

return TryCreateFromDelimitedString(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(delimitedString)), out result);
}

/// <summary>
/// Creates a new IndexUtilizationInfo from the backend delimited string.
/// </summary>
/// <param name="delimitedString">The backend delimited string to deserialize from.</param>
/// <param name="result">The parsed index utilization info</param>
/// <returns>A new IndexUtilizationInfo from the backend delimited string.</returns>
internal static bool TryCreateFromDelimitedString(string delimitedString, out IndexUtilizationInfo result)
{
if (delimitedString == null)
{
result = IndexUtilizationInfo.Empty;
return true;
}

// Even though this parsing is resilient, older version of the SDK doesn't have such lenient parsing.
// As such, it is right not not possible to remove some of the field in the IndexUtilizationInfo class.
// However, in newer version of the SDKs, the code base is going to start returning IndexMetricsInfo,
// so this class exists solely for legacy support.
try
{
result = JsonConvert.DeserializeObject<IndexUtilizationInfo>(delimitedString, new JsonSerializerSettings()
string decodedString = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(delimitedString));

result = JsonConvert.DeserializeObject<IndexUtilizationInfo>(decodedString, new JsonSerializerSettings()
{
// Allowing null values to be resilient to Json structure change
MissingMemberHandling = MissingMemberHandling.Ignore,
Expand All @@ -108,20 +98,10 @@ internal static bool TryCreateFromDelimitedString(string delimitedString, out In
/// Materialize the Index Utilization String into Concrete objects.
/// </summary>
/// <param name="delimitedString">The index utilization response string as sent by the back end.</param>
/// <param name="isBse64Encoded">The encoding of the string.</param>
/// <returns>Cpncrete Index utilization object.</returns>
public static IndexUtilizationInfo CreateFromString(string delimitedString, bool isBse64Encoded)
public static IndexUtilizationInfo CreateFromString(string delimitedString)
{
IndexUtilizationInfo indexUtilizationInfo;

if (isBse64Encoded)
{
TryCreateFromDelimitedBase64String(delimitedString, out indexUtilizationInfo);
}
else
{
TryCreateFromDelimitedString(delimitedString, out indexUtilizationInfo);
}
TryCreateFromDelimitedBase64String(delimitedString, out IndexUtilizationInfo indexUtilizationInfo);

return indexUtilizationInfo;
}
Expand Down
Loading