Skip to content

Commit

Permalink
Diagnostics: Adds Api for getting all regions contacted by a request (#…
Browse files Browse the repository at this point in the history
…2312)

* Api for getting all regions contacted by a request

* Updating public contract

* Updating tuple names

* Adding Emulator Test

* Renaming tuple

* optimizing tree search

Co-authored-by: j82w <j82w@users.noreply.github.com>
  • Loading branch information
asketagarwal and j82w committed Mar 17, 2021
1 parent da197a5 commit 8686eaa
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 9 deletions.
7 changes: 7 additions & 0 deletions Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;

/// <summary>
/// Contains the cosmos diagnostic information for the current request to Azure Cosmos DB service.
Expand All @@ -27,5 +28,11 @@ public virtual TimeSpan GetClientElapsedTime()
/// </summary>
/// <returns>The string field <see cref="CosmosDiagnostics"/> instance in the Azure CosmosDB database service.</returns>
public abstract override string ToString();

/// <summary>
/// Gets the list of all regions that were contacted for a request
/// </summary>
/// <returns>The list of tuples containing the Region name and the URI</returns>
public abstract IReadOnlyList<(string regionName, Uri uri)> GetContactedRegions();
}
}
28 changes: 28 additions & 0 deletions Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
namespace Microsoft.Azure.Cosmos.Diagnostics
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Azure.Cosmos.Json;
using Microsoft.Azure.Cosmos.Tracing;
using Microsoft.Azure.Cosmos.Tracing.TraceData;

internal sealed class CosmosTraceDiagnostics : CosmosDiagnostics
{
Expand Down Expand Up @@ -40,6 +43,31 @@ public override TimeSpan GetClientElapsedTime()
return this.Value.Duration;
}

public override IReadOnlyList<(string regionName, Uri uri)> GetContactedRegions()
{
HashSet<(string, Uri)> regionsContacted = new HashSet<(string, Uri)>();
ITrace rootTrace = this.Value;
this.WalkTraceTreeForRegionsContated(rootTrace, regionsContacted);
return regionsContacted.ToList();
}

private void WalkTraceTreeForRegionsContated(ITrace currentTrace, HashSet<(string, Uri)> regionsContacted)
{
foreach (object datums in currentTrace.Data.Values)
{
if (datums is ClientSideRequestStatisticsTraceDatum clientSideRequestStatisticsTraceDatum)
{
regionsContacted.UnionWith(clientSideRequestStatisticsTraceDatum.RegionsContactedWithName);
return;
}
}

foreach (ITrace childTrace in currentTrace.Children)
{
this.WalkTraceTreeForRegionsContated(childTrace, regionsContacted);
}
}

private string ToJsonString()
{
ReadOnlyMemory<byte> utf8String = this.WriteTraceToJsonWriter(JsonSerializationFormat.Text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public ClientSideRequestStatisticsTraceDatum(DateTime startTime)
this.ContactedReplicas = new List<Uri>();
this.StoreResponseStatisticsList = new List<StoreResponseStatistics>();
this.FailedReplicas = new HashSet<Uri>();
this.RegionsContacted = new HashSet<Uri>();
this.RegionsContactedWithName = new HashSet<(string, Uri)>();
this.clientSideRequestStatisticsCreateTime = Stopwatch.GetTimestamp();
}

Expand All @@ -49,6 +49,8 @@ public ClientSideRequestStatisticsTraceDatum(DateTime startTime)

public HashSet<Uri> RegionsContacted { get; }

public HashSet<(string, Uri)> RegionsContactedWithName { get; }

public TimeSpan RequestLatency
{
get
Expand Down Expand Up @@ -119,6 +121,7 @@ public void RecordResponse(DocumentServiceRequest request, StoreResult storeResu

DateTime responseTime = DateTime.UtcNow;
Uri locationEndpoint = request.RequestContext.LocationEndpointToRoute;
string regionName = request.RequestContext.RegionName;
StoreResponseStatistics responseStatistics = new StoreResponseStatistics(
startDateTime,
responseTime,
Expand All @@ -141,7 +144,7 @@ public void RecordResponse(DocumentServiceRequest request, StoreResult storeResu

if (locationEndpoint != null)
{
this.RegionsContacted.Add(locationEndpoint);
this.RegionsContactedWithName.Add((regionName, locationEndpoint));
}

this.StoreResponseStatisticsList.Add(responseStatistics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public void Visit(ClientSideRequestStatisticsTraceDatum clientSideRequestStatist

this.WriteJsonUriArrayWithDuplicatesCounted("ContactedReplicas", clientSideRequestStatisticsTraceDatum.ContactedReplicas);

this.WriteJsonUriArray("RegionsContacted", clientSideRequestStatisticsTraceDatum.RegionsContacted);
this.WriteRegionsContactedArray("RegionsContacted", clientSideRequestStatisticsTraceDatum.RegionsContactedWithName);
this.WriteJsonUriArray("FailedReplicas", clientSideRequestStatisticsTraceDatum.FailedReplicas);

this.jsonWriter.WriteFieldName("AddressResolutionStatistics");
Expand Down Expand Up @@ -321,6 +321,22 @@ private void WriteJsonUriArray(string propertyName, IEnumerable<Uri> uris)
this.jsonWriter.WriteArrayEnd();
}

private void WriteRegionsContactedArray(string propertyName, IEnumerable<(string, Uri)> uris)
{
this.jsonWriter.WriteFieldName(propertyName);
this.jsonWriter.WriteArrayStart();

if (uris != null)
{
foreach ((string _, Uri uri) contactedRegion in uris)
{
this.WriteStringValueOrNull(contactedRegion.uri?.ToString());
}
}

this.jsonWriter.WriteArrayEnd();
}

/// <summary>
/// Writes the list of URIs to JSON.
/// Sequential duplicates are counted and written as a single object to prevent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2433,6 +2433,18 @@ public async Task CustomPropertiesItemRequestOptionsTest()
Assert.AreEqual(HttpStatusCode.Created, responseAstype.StatusCode);
}

[TestMethod]
public async Task RegionsContactedTest()
{
ToDoActivity item = ToDoActivity.CreateRandomToDoActivity();
ItemResponse<ToDoActivity> response = await this.Container.CreateItemAsync<ToDoActivity>(item, new Cosmos.PartitionKey(item.pk));
Assert.IsNotNull(response.Diagnostics);
IReadOnlyList<(string region, Uri uri)> regionsContacted = response.Diagnostics.GetContactedRegions();
Assert.AreEqual(regionsContacted.Count, 1);
Assert.AreEqual(regionsContacted[0].region, Regions.SouthCentralUS);
Assert.IsNotNull(regionsContacted[0].uri);
}

#if PREVIEW
[TestMethod]
public async Task VerifyDocumentCrudWithMultiHashKind()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@
datum.FailedReplicas.Add(uri1);
datum.FailedReplicas.Add(uri2);
datum.RegionsContacted.Add(uri1);
datum.RegionsContacted.Add(uri2);
datum.RegionsContactedWithName.Add(("local", uri1));
datum.RegionsContactedWithName.Add(("local", uri2));
datum.RequestEndTimeUtc = DateTime.MaxValue;
Expand Down Expand Up @@ -377,7 +377,7 @@
datum.FailedReplicas.Add(default);
datum.RegionsContacted.Add(default);
datum.RegionsContactedWithName.Add(default);
datum.RequestEndTimeUtc = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1936,6 +1936,11 @@
"Microsoft.Azure.Cosmos.CosmosDiagnostics;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
"System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.Uri]] GetContactedRegions()": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "System.Collections.Generic.IReadOnlyList`1[System.ValueTuple`2[System.String,System.Uri]] GetContactedRegions();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.String ToString()": {
"Type": "Method",
"Attributes": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace Microsoft.Azure.Cosmos.Tests.Tracing
{
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Azure.Cosmos.Diagnostics;
using Microsoft.Azure.Cosmos.Tracing;
using Microsoft.Azure.Cosmos.Tracing.TraceData;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class ContactedRegionsTests
{
[TestMethod]
public void ContactedRegionsTest()
{
CosmosDiagnostics diagnostics = new CosmosTraceDiagnostics(this.CreateTestTraceTree());
IReadOnlyList<(string, Uri)> regionsContacted = diagnostics.GetContactedRegions();
Assert.IsNotNull(regionsContacted);
Assert.AreEqual(regionsContacted.Count, 4);
}

private ITrace CreateTestTraceTree()
{
ITrace trace;
using (trace = Trace.GetRootTrace("Root Trace", TraceComponent.Unknown, TraceLevel.Info))
{
using (ITrace firstLevel = trace.StartChild("First level Node", TraceComponent.Unknown, TraceLevel.Info))
{
using (ITrace secondLevel = trace.StartChild("Second level Node", TraceComponent.Unknown, TraceLevel.Info))
{
using (ITrace thirdLevel = trace.StartChild("Third level Node", TraceComponent.Unknown, TraceLevel.Info))
{
thirdLevel.AddDatum("Client Side Request Stats", this.GetDatumObject(Regions.CentralUS));
}
}

using (ITrace secondLevel = trace.StartChild("Second level Node", TraceComponent.Unknown, TraceLevel.Info))
{
secondLevel.AddDatum("Client Side Request Stats", this.GetDatumObject(Regions.CentralIndia, Regions.EastUS2));
}
}

using (ITrace firstLevel = trace.StartChild("First level Node", TraceComponent.Unknown, TraceLevel.Info))
{
firstLevel.AddDatum("Client Side Request Stats", this.GetDatumObject(Regions.FranceCentral));
}
}

return trace;
}

private TraceDatum GetDatumObject(string regionName1, string regionName2 = null)
{
ClientSideRequestStatisticsTraceDatum datum = new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow);
Uri uri1 = new Uri("http://someUri1.com");
datum.RegionsContactedWithName.Add((regionName1, uri1));
if (regionName2 != null)
{
Uri uri2 = new Uri("http://someUri2.com");
datum.RegionsContactedWithName.Add((regionName2, uri2));
}

return datum;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,8 @@ public void TraceData()
datum.FailedReplicas.Add(uri1);
datum.FailedReplicas.Add(uri2);

datum.RegionsContacted.Add(uri1);
datum.RegionsContacted.Add(uri2);
datum.RegionsContactedWithName.Add(("local", uri1));
datum.RegionsContactedWithName.Add(("local", uri2));

datum.RequestEndTimeUtc = DateTime.MaxValue;

Expand Down Expand Up @@ -426,7 +426,7 @@ public void TraceData()

datum.FailedReplicas.Add(default);

datum.RegionsContacted.Add(default);
datum.RegionsContactedWithName.Add(default);

datum.RequestEndTimeUtc = default;

Expand Down

0 comments on commit 8686eaa

Please sign in to comment.