diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs
index 8e8749374f..5734723c4d 100644
--- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs
+++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs
@@ -4,6 +4,7 @@
namespace Microsoft.Azure.Cosmos
{
using System;
+ using System.Collections.Generic;
///
/// Contains the cosmos diagnostic information for the current request to Azure Cosmos DB service.
@@ -27,5 +28,11 @@ public virtual TimeSpan GetClientElapsedTime()
///
/// The string field instance in the Azure CosmosDB database service.
public abstract override string ToString();
+
+ ///
+ /// Gets the list of all regions that were contacted for a request
+ ///
+ /// The list of tuples containing the Region name and the URI
+ public abstract IReadOnlyList<(string regionName, Uri uri)> GetContactedRegions();
}
}
diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs
index cb2f5b6b2a..ae7245d0eb 100644
--- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs
+++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosTraceDiagnostics.cs
@@ -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
{
@@ -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 utf8String = this.WriteTraceToJsonWriter(JsonSerializationFormat.Text);
diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs
index 1515069006..2c95b4d2c3 100644
--- a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs
+++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs
@@ -29,7 +29,7 @@ public ClientSideRequestStatisticsTraceDatum(DateTime startTime)
this.ContactedReplicas = new List();
this.StoreResponseStatisticsList = new List();
this.FailedReplicas = new HashSet();
- this.RegionsContacted = new HashSet();
+ this.RegionsContactedWithName = new HashSet<(string, Uri)>();
this.clientSideRequestStatisticsCreateTime = Stopwatch.GetTimestamp();
}
@@ -49,6 +49,8 @@ public ClientSideRequestStatisticsTraceDatum(DateTime startTime)
public HashSet RegionsContacted { get; }
+ public HashSet<(string, Uri)> RegionsContactedWithName { get; }
+
public TimeSpan RequestLatency
{
get
@@ -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,
@@ -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);
diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs
index 144b5fee5b..6a06f354ae 100644
--- a/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs
+++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceWriter.TraceJsonWriter.cs
@@ -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");
@@ -321,6 +321,22 @@ private void WriteJsonUriArray(string propertyName, IEnumerable 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();
+ }
+
///
/// Writes the list of URIs to JSON.
/// Sequential duplicates are counted and written as a single object to prevent
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs
index 38ddcf1da2..297420a729 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs
@@ -2433,6 +2433,18 @@ public async Task CustomPropertiesItemRequestOptionsTest()
Assert.AreEqual(HttpStatusCode.Created, responseAstype.StatusCode);
}
+ [TestMethod]
+ public async Task RegionsContactedTest()
+ {
+ ToDoActivity item = ToDoActivity.CreateRandomToDoActivity();
+ ItemResponse response = await this.Container.CreateItemAsync(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()
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml
index a6c4cd071e..e2e2c85b9d 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/TraceWriterBaselineTests.TraceData.xml
@@ -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;
@@ -377,7 +377,7 @@
datum.FailedReplicas.Add(default);
- datum.RegionsContacted.Add(default);
+ datum.RegionsContactedWithName.Add(default);
datum.RequestEndTimeUtc = default;
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 a6544b2654..31507703ae 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
@@ -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": [],
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/ContactedRegionsTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/ContactedRegionsTests.cs
new file mode 100644
index 0000000000..34d8469829
--- /dev/null
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/ContactedRegionsTests.cs
@@ -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;
+ }
+ }
+}
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs
index dfda2d8ef7..c2a5f0285d 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs
@@ -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;
@@ -426,7 +426,7 @@ public void TraceData()
datum.FailedReplicas.Add(default);
- datum.RegionsContacted.Add(default);
+ datum.RegionsContactedWithName.Add(default);
datum.RequestEndTimeUtc = default;