From 5ec7b4bcd7e2e0698736cd374f2196682227c913 Mon Sep 17 00:00:00 2001 From: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:26:04 -0700 Subject: [PATCH 01/20] Code changes to update release note. (#3996) --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 481cde52db..ab733a4137 100644 --- a/changelog.md +++ b/changelog.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Added - [3668](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3668) Query : Adds string comparison alternative when converting LINQ to SQL (Thanks [@ernesto1596](https://github.com/ernesto1596)) - [3834](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3834) Query : Adds support for newtonsoft member access via ExtensionData (Thanks [@onionhammer](https://github.com/onionhammer)) +- [3939](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3939) CreateAndInitializeAsync: Adds Code to Optimize Rntbd Open Connection Logic to Open Connections in Parallel ### [3.35.1-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.1-preview) - 2023-06-27 ### [3.35.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.1) - 2023-06-27 From 08981bf2fd87f110ca4a1e2af5f82974857c1aaf Mon Sep 17 00:00:00 2001 From: vipulvishal-ms <110802706+vipulvishal-ms@users.noreply.github.com> Date: Sat, 29 Jul 2023 02:33:38 +0530 Subject: [PATCH 02/20] Client Encryption: Adds fix for supporting Prefix Partition Key (Hierarchical partitioning) (#3979) * Hirarchical pk bug fix * Hirarchical pk bug fix * Hirarchical pk bug fix * Hirarchical pk bug fix * Hirarchical pk bug fix * testing new version * adding more tests * adding more tests * adding more tests * code review changes * test fix * test fix * test fix * test fix --------- Co-authored-by: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> --- Directory.Build.props | 4 +- .../changelog.md | 10 + .../src/EncryptionContainer.cs | 17 +- .../Microsoft.Azure.Cosmos.Encryption.csproj | 4 +- .../tests/EmulatorTests/MdeEncryptionTests.cs | 388 +++++++++++++++++- 5 files changed, 413 insertions(+), 10 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index b9171d6307..9daa1e7e9f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,8 +4,8 @@ 3.35.2 preview 3.31.3 - 2.0.2 - 2.0.2 + 2.0.3 + 2.0.3 preview 1.0.0-preview06 1.1.0-preview3 diff --git a/Microsoft.Azure.Cosmos.Encryption/changelog.md b/Microsoft.Azure.Cosmos.Encryption/changelog.md index a8edf3441d..2f50fcbdf3 100644 --- a/Microsoft.Azure.Cosmos.Encryption/changelog.md +++ b/Microsoft.Azure.Cosmos.Encryption/changelog.md @@ -3,6 +3,16 @@ Preview features are treated as a separate branch and will not be included in th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +### [2.0.3](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.0.3) - 2023-07-12 + +#### Added +- [#3979](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3979) Adds fix for supporting Prefix Partition Key (Hierarchical partitioning). + +### [2.0.3-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.0.3-preview) - 2023-07-12 + +#### Added +- [#3979](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3979) Adds fix for supporting Prefix Partition Key (Hierarchical partitioning). + ### [2.0.2](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.0.2) - 2023-06-01 #### Added diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs index d32bfa1c94..0bd342f0ef 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainer.cs @@ -957,15 +957,22 @@ internal async Task CheckIfIdIsEncryptedAndGetEncryptedIdAsync( JArray jArray = JArray.Parse(partitionKey.ToString()); #if ENCRYPTIONPREVIEW - if (jArray.Count > 1) + if (encryptionSettings.PartitionKeyPaths.Count > 1) { - int i = 0; + int counter = 0; PartitionKeyBuilder partitionKeyBuilder = new PartitionKeyBuilder(); + if (jArray.Count() > encryptionSettings.PartitionKeyPaths.Count()) + { + throw new NotSupportedException($"The number of partition keys passed in the query exceeds the number of keys initialized on the container. Container Id : {this.Id}"); + } bool isPkEncrypted = false; + // partitionKeyBuilder expects the paths and values to be in same order. - foreach (string path in encryptionSettings.PartitionKeyPaths) + for(counter = 0; counter < jArray.Count(); counter++) { + string path = encryptionSettings.PartitionKeyPaths[counter]; + // case: partition key path is /a/b/c and the client encryption policy has /a in path. // hence encrypt the partition key value with using its top level path /a since /c would have been encrypted in the document using /a's policy. string partitionKeyPath = path.Split('/')[1]; @@ -975,12 +982,12 @@ internal async Task CheckIfIdIsEncryptedAndGetEncryptedIdAsync( if (encryptionSettingForProperty == null) { - partitionKeyBuilder.Add(jArray[i++].ToString()); + partitionKeyBuilder.Add(jArray[counter].ToString()); continue; } isPkEncrypted = true; - Stream valueStream = EncryptionProcessor.BaseSerializer.ToStream(jArray[i++]); + Stream valueStream = EncryptionProcessor.BaseSerializer.ToStream(jArray[counter]); Stream encryptedPartitionKey = await EncryptionProcessor.EncryptValueStreamAsync( valueStreamToEncrypt: valueStream, diff --git a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj index 94e93aa6fe..f7baa75f60 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj +++ b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj @@ -28,11 +28,11 @@ - + - + diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs index 65efd425aa..3489cdf02e 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs @@ -189,6 +189,9 @@ public void TestInitialize() { // Reset static cache TTL Microsoft.Data.Encryption.Cryptography.ProtectedDataEncryptionKey.TimeToLive = TimeSpan.FromHours(2); + // flag to disable https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3951 + // need to be removed after the fix + Environment.SetEnvironmentVariable("AZURE_COSMOS_REPLICA_VALIDATION_ENABLED", "False"); } private static async Task CreateClientEncryptionKeyAsync(string cekId, Cosmos.EncryptionKeyWrapMetadata encryptionKeyWrapMetadata) @@ -2303,7 +2306,7 @@ public async Task ValidatePkAndIdEncryptionSupport() VerifyExpectedDocResponse(testDoc, readResponse.Resource); #if ENCRYPTIONTESTPREVIEW - // hierarchical + // hierarchical pk container test cepWithPKIdPath1 = new ClientEncryptionIncludedPath() { Path = "/Sensitive_LongFormat", @@ -2352,9 +2355,320 @@ public async Task ValidatePkAndIdEncryptionSupport() Assert.AreEqual(HttpStatusCode.OK, readResponse.StatusCode); VerifyExpectedDocResponse(testDoc, readResponse.Resource); + + // test to validate query with one partition key (topmost) in hierarchical pk container of 3 keys + QueryRequestOptions queryRequestOptions = new QueryRequestOptions + { + PartitionKey = new PartitionKeyBuilder().Add(testDoc.Sensitive_StringFormat).Build() + }; + + using FeedIterator setIterator = encryptionContainer.GetItemQueryIterator("select * from c", requestOptions: queryRequestOptions); + + while (setIterator.HasMoreResults) + { + FeedResponse response = await setIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + + // test to validate query with one partition key (topmost) in hierarchical pk container of 3 keys with where clause on topmost pk + QueryDefinition queryDefinition = encryptionContainer.CreateQueryDefinition("SELECT * FROM c WHERE c.Sensitive_StringFormat = @Sensitive_StringFormat"); + + await queryDefinition.AddParameterAsync("@Sensitive_StringFormat", testDoc.Sensitive_StringFormat, "/Sensitive_StringFormat"); + + FeedIterator setIteratorWithFilter = encryptionContainer.GetItemQueryIterator(queryDefinition, requestOptions: queryRequestOptions); + + while (setIteratorWithFilter.HasMoreResults) + { + FeedResponse response = await setIteratorWithFilter.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + + // test to validate query with one partition key (2nd topmost) in hierarchical pk container of 3 keys with where clause on topmost pk + // this shold give 0 items as PK is set wrongly + queryRequestOptions = new QueryRequestOptions + { + PartitionKey = new PartitionKeyBuilder().Add(testDoc.Sensitive_NestedObjectFormatL1.Sensitive_NestedObjectFormatL2.Sensitive_StringFormatL2).Build() + }; + + setIteratorWithFilter = encryptionContainer.GetItemQueryIterator(queryDefinition, requestOptions: queryRequestOptions); + + while (setIteratorWithFilter.HasMoreResults) + { + FeedResponse response = await setIteratorWithFilter.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual(0, response.Count()); + } #endif } +#if ENCRYPTIONTESTPREVIEW + [TestMethod] + public async Task TestHirarchicalPkWithFullAndPartialKey() + { + HirarchicalPkTestDoc testDoc = HirarchicalPkTestDoc.Create(); + + ClientEncryptionIncludedPath cepWithPKIdPath1 = new ClientEncryptionIncludedPath() + { + Path = "/State", + ClientEncryptionKeyId = "key1", + EncryptionType = "Deterministic", + EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", + }; + + ClientEncryptionIncludedPath cepWithPKIdPath2 = new ClientEncryptionIncludedPath() + { + Path = "/City", + ClientEncryptionKeyId = "key1", + EncryptionType = "Deterministic", + EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", + }; + + ClientEncryptionIncludedPath cepWithPKIdPath3 = new ClientEncryptionIncludedPath() + { + Path = "/ZipCode", + ClientEncryptionKeyId = "key1", + EncryptionType = "Deterministic", + EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", + }; + + Collection paths = new Collection { cepWithPKIdPath1, cepWithPKIdPath2, cepWithPKIdPath3 }; + + ClientEncryptionPolicy clientEncryptionPolicy= new ClientEncryptionPolicy(paths, 2); + + ContainerProperties containerProperties = new ContainerProperties() + { + Id = "HierarchicalPkContainerWith3Pk", + PartitionKeyPaths = new List { "/State", "/City", "/ZipCode" }, + ClientEncryptionPolicy = clientEncryptionPolicy + }; + + Container encryptionContainer = await database.CreateContainerAsync(containerProperties, 400); + await encryptionContainer.InitializeEncryptionAsync(); + + PartitionKey hirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.State) + .Add(testDoc.City) + .Add(testDoc.ZipCode) + .Build(); + + ItemResponse createResponse = await encryptionContainer.CreateItemAsync( + testDoc, + partitionKey: hirarchicalPk); + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + VerifyExpectedDocResponse(testDoc, createResponse.Resource); + + // read back + ItemResponse readResponse = await encryptionContainer.ReadItemAsync( + testDoc.Id, + hirarchicalPk); + + Assert.AreEqual(HttpStatusCode.OK, readResponse.StatusCode); + VerifyExpectedDocResponse(testDoc, readResponse.Resource); + + PartitionKey partialHirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.State) + .Add(testDoc.City) + .Build(); + + QueryRequestOptions queryRequestOptions = new QueryRequestOptions + { + PartitionKey = partialHirarchicalPk + }; + + using FeedIterator setIterator = encryptionContainer.GetItemQueryIterator("select * from c", requestOptions: queryRequestOptions); + + while (setIterator.HasMoreResults) + { + FeedResponse response = await setIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + + QueryDefinition withEncryptedParameter = encryptionContainer.CreateQueryDefinition( + "SELECT * FROM c WHERE c.City = @cityInput AND c.State = @stateInput"); + + await withEncryptedParameter.AddParameterAsync( + "@cityInput", + testDoc.City, + "/City"); + + await withEncryptedParameter.AddParameterAsync( + "@stateInput", + testDoc.State, + "/State"); + + // query with partial HirarchicalPk state and city + FeedIterator queryResponseIterator; + queryResponseIterator = encryptionContainer.GetItemQueryIterator(withEncryptedParameter, requestOptions: queryRequestOptions); + + while (queryResponseIterator.HasMoreResults) + { + FeedResponse response = await queryResponseIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + + partialHirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.State) + .Build(); + + queryRequestOptions = new QueryRequestOptions + { + PartitionKey = partialHirarchicalPk + }; + + // query with partial HirarchicalPk state + queryResponseIterator = encryptionContainer.GetItemQueryIterator(withEncryptedParameter, requestOptions: queryRequestOptions); + + while (queryResponseIterator.HasMoreResults) + { + FeedResponse response = await queryResponseIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + + partialHirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.ZipCode) + .Build(); + + queryRequestOptions = new QueryRequestOptions + { + PartitionKey = partialHirarchicalPk + }; + + // query with partial HirarchicalPk zipCode. + // Since zipCode is 3rd in HirarchicalPk set. Query will get 0 response. + queryResponseIterator = encryptionContainer.GetItemQueryIterator(withEncryptedParameter, requestOptions: queryRequestOptions); + + while (queryResponseIterator.HasMoreResults) + { + FeedResponse response = await queryResponseIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual(0, response.Count()); + } + + // query with no HirarchicalPk set. + queryResponseIterator = encryptionContainer.GetItemQueryIterator(withEncryptedParameter); + + while (queryResponseIterator.HasMoreResults) + { + FeedResponse response = await queryResponseIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + + partialHirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.State) + .Add(testDoc.City) + .Add(testDoc.ZipCode) + .Add("Extra Value") + .Build(); + + queryRequestOptions = new QueryRequestOptions + { + PartitionKey = partialHirarchicalPk + }; + // query with more PKs greater than number of PK feilds set in the container settings. + try + { + queryResponseIterator = encryptionContainer.GetItemQueryIterator(withEncryptedParameter, requestOptions: queryRequestOptions); + while (queryResponseIterator.HasMoreResults) + { + FeedResponse response = await queryResponseIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual(0, response.Count()); + } + } + catch (Exception ex) + { + Assert.IsTrue(ex is NotSupportedException); + if (ex is NotSupportedException notSupportedException) + Assert.IsTrue(notSupportedException.Message.Contains("The number of partition keys passed in the query exceeds the number of keys initialized on the container")); + } + } + + [TestMethod] + public async Task TestHirarchicalPkWithOnlyOneKey() + { + HirarchicalPkTestDoc testDoc = HirarchicalPkTestDoc.Create(); + + ClientEncryptionIncludedPath cepWithPKIdPath1 = new ClientEncryptionIncludedPath() + { + Path = "/State", + ClientEncryptionKeyId = "key1", + EncryptionType = "Deterministic", + EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", + }; + + ClientEncryptionIncludedPath cepWithPKIdPath2 = new ClientEncryptionIncludedPath() + { + Path = "/City", + ClientEncryptionKeyId = "key1", + EncryptionType = "Deterministic", + EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", + }; + + ClientEncryptionIncludedPath cepWithPKIdPath3 = new ClientEncryptionIncludedPath() + { + Path = "/ZipCode", + ClientEncryptionKeyId = "key1", + EncryptionType = "Deterministic", + EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256", + }; + + Collection paths = new Collection { cepWithPKIdPath1, cepWithPKIdPath2, cepWithPKIdPath3 }; + + ClientEncryptionPolicy clientEncryptionPolicy = new ClientEncryptionPolicy(paths, 2); + + ContainerProperties containerProperties = new ContainerProperties() + { + Id = "HierarchicalPkContainerWithOnePk", + PartitionKeyPaths = new List { "/State" }, + ClientEncryptionPolicy = clientEncryptionPolicy + }; + + Container encryptionContainer = await database.CreateContainerAsync(containerProperties, 400); + await encryptionContainer.InitializeEncryptionAsync(); + + PartitionKey hirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.State) + .Build(); + + ItemResponse createResponse = await encryptionContainer.CreateItemAsync( + testDoc, + partitionKey: hirarchicalPk); + Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); + VerifyExpectedDocResponse(testDoc, createResponse.Resource); + + // read back + ItemResponse readResponse = await encryptionContainer.ReadItemAsync( + testDoc.Id, + hirarchicalPk); + + Assert.AreEqual(HttpStatusCode.OK, readResponse.StatusCode); + VerifyExpectedDocResponse(testDoc, readResponse.Resource); + + PartitionKey fullHirarchicalPk = new PartitionKeyBuilder() + .Add(testDoc.State) + .Build(); + + QueryRequestOptions queryRequestOptions = new QueryRequestOptions + { + PartitionKey = fullHirarchicalPk + }; + + using FeedIterator setIterator = encryptionContainer.GetItemQueryIterator("select * from c", requestOptions: queryRequestOptions); + + while (setIterator.HasMoreResults) + { + FeedResponse response = await setIterator.ReadNextAsync().ConfigureAwait(false); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + VerifyExpectedDocResponse(testDoc, response.First()); + } + } +#endif [TestMethod] public async Task EncryptionStreamIteratorValidation() { @@ -3204,6 +3518,14 @@ private static async Task> MdeDeleteItemAsync( return deleteResponse; } + private static void VerifyExpectedDocResponse(HirarchicalPkTestDoc expectedDoc, HirarchicalPkTestDoc verifyDoc) + { + Assert.AreEqual(expectedDoc.Id, verifyDoc.Id); + Assert.AreEqual(expectedDoc.State, verifyDoc.State); + Assert.AreEqual(expectedDoc.City, verifyDoc.City); + Assert.AreEqual(expectedDoc.ZipCode, verifyDoc.ZipCode); + } + private static void VerifyExpectedDocResponse(TestDoc expectedDoc, TestDoc verifyDoc) { Assert.AreEqual(expectedDoc.Id, verifyDoc.Id); @@ -3716,6 +4038,70 @@ public Stream ToStream() } } + public class HirarchicalPkTestDoc + { + [JsonProperty("id")] + public string Id { get; set; } + + public string PK { get; set; } + + public string State { get; set; } + + public string City { get; set; } + + public string ZipCode { get; set; } + + public HirarchicalPkTestDoc() + { + } + public HirarchicalPkTestDoc(HirarchicalPkTestDoc other) + { + this.Id = other.Id; + this.PK = other.PK; + this.State = other.State; + this.City = other.City; + this.ZipCode = other.ZipCode; + } + + public override bool Equals(object obj) + { + return obj is HirarchicalPkTestDoc doc + && this.Id == doc.Id + && this.PK == doc.PK + && this.State == doc.State + && this.City == doc.City + && this.ZipCode == doc.ZipCode; + } + + public override int GetHashCode() + { + int hashCode = 1652434776; + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Id); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.PK); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.State); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.City); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.ZipCode); + return hashCode; + } + + public static HirarchicalPkTestDoc Create(string partitionKey = null) + { + return new HirarchicalPkTestDoc() + { + Id = Guid.NewGuid().ToString(), + PK = partitionKey ?? Guid.NewGuid().ToString(), + State = Guid.NewGuid().ToString(), + City = Guid.NewGuid().ToString(), + ZipCode = Guid.NewGuid().ToString() + }; + } + + public Stream ToStream() + { + return TestCommon.ToStream(this); + } + } + internal class TestKeyEncryptionKey : IKeyEncryptionKey { private static readonly Dictionary keyinfo = new Dictionary From ebd1b91a26547721ef232ef597d878144889b80b Mon Sep 17 00:00:00 2001 From: akotalwar <94020786+akotalwar@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:28:49 -0700 Subject: [PATCH 03/20] Query: Refactors changelog.md with Optimistic Direct Execution recommendation (#4004) * Update changelog.md This is a recommendation for customers if they would like to use the ODE features. * Updated release notes for ODE --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index ab733a4137..a6f9e65864 100644 --- a/changelog.md +++ b/changelog.md @@ -44,6 +44,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [3836](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3836) Integrated cache: Adds BypassIntegratedCache to DedicatedGatewayRequestOptions - [3909](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3909) Query: Adds EnableOptimisticDirectExecution in QueryRequestOptions enabled by default +Recommendation for customers regarding Optimistic Direct Execution: + +Starting Version 3.35.0, the Preview SDK enables the ODE feature by default. This can potentially cause a new type of continuation token to be generated. Such a token is not recognized by the older SDKs by design and this could result in a Malformed Continuation Token Exception. +If you have a scenario where tokens generated from the newer SDKs are used by an older SDK, we recommend a 2 step approach to upgrade: + +- Upgrade to the new SDK and disable ODE, both together as part of a single deployment. Wait for all nodes to upgrade. + - In order to disable ODE, set EnableOptimisticDirectExecution to false in the QueryRequestOptions. +- Enable ODE as part of second deployment for all nodes. + ### [3.35.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.0) - 2023-06-19 #### Fixed From 8c4f99f35e326b61ecca210988334d00629b6ee7 Mon Sep 17 00:00:00 2001 From: akotalwar <94020786+akotalwar@users.noreply.github.com> Date: Sat, 5 Aug 2023 13:01:59 -0700 Subject: [PATCH 04/20] [Internal] Query: Adds performance testing for OptimisticDirectExecution pipeline (#3839) * Infrastructure for performance testing with ODE pipeline. * Resolve comments * Removed randomization from data creation process * Fixed comments * Removed Query and EnableODE from QueryStatisticsMetrics, as they do not relate to query statistics. * Removed try catch to make CreateItemAsync call always succeed * Removed one liner functions * Removed code from MetricsSerializer and QueryStatisticsDatumVisitor files * Fixed comments * Removed request Charge check * Bug in Debug Assert * Test * Bug in debug assert fix * Fixed second bug in Metrics Accumalator class * Added ignore flag to ode perf tests so that they do not run on every loop build * Added comment explaining the Ignore flag. --- .../ContentSerializationPerformanceTests.cs | 18 +- .../QueryPerfTest/MetricsAccumulator.cs | 8 +- ...timisticDirectExecutionPerformanceTests.cs | 469 ++++++++++++++++++ .../QueryPerfTest/QueryStatisticsMetrics.cs | 6 + .../settings.json | 18 +- 5 files changed, 496 insertions(+), 23 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/OptimisticDirectExecutionPerformanceTests.cs diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/ContentSerializationPerformanceTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/ContentSerializationPerformanceTests.cs index 759e63b345..8ff5660c6f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/ContentSerializationPerformanceTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/ContentSerializationPerformanceTests.cs @@ -33,15 +33,15 @@ public ContentSerializationPerformanceTests() this.queryStatisticsDatumVisitor = new(); this.endpoint = Utils.ConfigurationManager.AppSettings["GatewayEndpoint"]; this.authKey = Utils.ConfigurationManager.AppSettings["MasterKey"]; - this.cosmosDatabaseId = Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.CosmosDatabaseId"]; - this.containerId = Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.ContainerId"]; - this.contentSerialization = Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.ContentSerialization"]; - this.query = Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.Query"]; - this.numberOfIterations = int.Parse(Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.NumberOfIterations"]); - this.warmupIterations = int.Parse(Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.WarmupIterations"]); - this.MaxConcurrency = int.Parse(Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.MaxConcurrency"]); - this.MaxItemCount = int.Parse(Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.MaxItemCount"]); - this.useStronglyTypedIterator = bool.Parse(Utils.ConfigurationManager.AppSettings["ContentSerializationPerformanceTests.UseStronglyTypedIterator"]); + this.cosmosDatabaseId = Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.CosmosDatabaseId"]; + this.containerId = Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.ContainerId"]; + this.contentSerialization = Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.ContentSerialization"]; + this.query = Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.Query"]; + this.numberOfIterations = int.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.NumberOfIterations"]); + this.warmupIterations = int.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.WarmupIterations"]); + this.MaxConcurrency = int.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.MaxConcurrency"]); + this.MaxItemCount = int.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.MaxItemCount"]); + this.useStronglyTypedIterator = bool.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.UseStronglyTypedIterator"]); } [TestMethod] diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/MetricsAccumulator.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/MetricsAccumulator.cs index 56fc16b683..a7e6032001 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/MetricsAccumulator.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/MetricsAccumulator.cs @@ -53,18 +53,16 @@ public void ReadFromTrace(FeedResponse Response, QueryStatisticsDatumVisit { if (storeResponse.StoreResult.StatusCode == StatusCodes.Ok) { - backendAndClientMetrics.Add(Tuple.Create(retrieveCosmosElementTraces[k], backendMetrics[j], transitMetrics[i])); + backendAndClientMetrics.Add(Tuple.Create(retrieveCosmosElementTraces[k], backendMetrics[j], node)); j++; k++; } else { //We add null values to the tuple since status codes other than Ok will not have data for 'Query Metrics' and 'Get Cosmos Element Response' - backendAndClientMetrics.Add(Tuple.Create(null, null, transitMetrics[i])); + backendAndClientMetrics.Add(Tuple.Create(null, null, node)); } } - - i++; } Debug.Assert(i == transitMetrics.Count, "All 'transit metrics' must be grouped."); @@ -76,7 +74,7 @@ public void ReadFromTrace(FeedResponse Response, QueryStatisticsDatumVisit { if (metrics.Item2 != null) { - Debug.Assert(metrics.Item1 == null, "'Get Cosmos Element Response' is null"); + Debug.Assert(metrics.Item1 != null, "'Get Cosmos Element Response' is null"); queryStatisticsDatumVisitor.AddGetCosmosElementResponseTime(metrics.Item1.Duration.TotalMilliseconds); foreach (KeyValuePair kvp in metrics.Item2.Data) { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/OptimisticDirectExecutionPerformanceTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/OptimisticDirectExecutionPerformanceTests.cs new file mode 100644 index 0000000000..756357e611 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/OptimisticDirectExecutionPerformanceTests.cs @@ -0,0 +1,469 @@ +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class OptimisticDirectExecutionPerformanceTests + { + private Container Container; + private const string RawDataFileName = "OptimisticDirectExecutionPerformanceTestsRawData.csv"; + private const string AggregateDataFileName = "OptimisticDirectExecutionPerformanceTestsAggregatedData.csv"; + private const string PrintQueryMetrics = "QueryMetrics"; + private static readonly string RawDataPath = Path.GetFullPath(RawDataFileName); + private static readonly string AggregateDataPath = Path.GetFullPath(AggregateDataFileName); + private static readonly string Endpoint = Utils.ConfigurationManager.AppSettings["GatewayEndpoint"]; + private static readonly string AuthKey = Utils.ConfigurationManager.AppSettings["MasterKey"]; + private static readonly string CosmosDatabaseId = Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.CosmosDatabaseId"]; + private static readonly string ContainerId = Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.ContainerId"]; + private static readonly PartitionKey PartitionKeyValue = new PartitionKey("Andersen"); + private static readonly int NumberOfIterations = int.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.NumberOfIterations"]); + private static readonly int WarmupIterations = int.Parse(Utils.ConfigurationManager.AppSettings["QueryPerformanceTests.WarmupIterations"]); + + [TestInitialize] + public async Task InitializeTest() + { + CosmosClientOptions clientOptions = new CosmosClientOptions() + { + ConnectionMode = ConnectionMode.Direct, + }; + + CosmosClient cosmosClient = new CosmosClient(Endpoint, AuthKey, clientOptions); + Database database = await cosmosClient.CreateDatabaseAsync(CosmosDatabaseId); + this.Container = await database.CreateContainerAsync(ContainerId, partitionKeyPath: "/name"); + await this.AddItemsToContainerAsync(this.Container); + + if (File.Exists(RawDataPath)) + { + File.Delete(RawDataPath); + } + + if (File.Exists(AggregateDataPath)) + { + File.Delete(AggregateDataPath); + } + } + + [TestCleanup] + public async Task CleanupAsync() + { + if (this.Container != null) + { + await this.Container.Database.DeleteAsync(); + this.Container = null; + } + } + + private async Task AddItemsToContainerAsync(Container container) + { + int totalItems = 5000; + string[] cityOptions = new string[] { "Seattle", "Chicago", "NYC", "SF" }; + int numberOfRecipeints = cityOptions.Length; + List recipientList = new List(numberOfRecipeints); + + for (int j = 0; j < numberOfRecipeints; j++) + { + RecipientList recipient = new RecipientList() + { + Name = "John", + City = cityOptions[j], + }; + + recipientList.Add(recipient); + } + + // Create a family object for the Andersen family + foreach (int i in Enumerable.Range(0, totalItems)) + { + States andersenFamily = new States + { + Id = i.ToString(), + Name = i < (totalItems/2) ? "Andersen" : "Smith", + City = cityOptions[i%cityOptions.Length], + PostalCode = (i * 10).ToString(), + Region = "Northwest", + UserDefinedID = i % 10, + RecipientList = recipientList + }; + + ItemResponse andersenFamilyResponse = await container.CreateItemAsync(andersenFamily, new PartitionKey(andersenFamily.Name)); + Console.WriteLine("Created item in database with id: {0} Operation consumed {1} RUs.\n", andersenFamilyResponse.Resource.Id, andersenFamilyResponse.RequestCharge); + } + } + + //Set Ode perf tests to ignore so that they dont run on every loop build. + //Ignore flag can be removed when checking for Ode performance. + [Ignore] + [TestMethod] + [Owner("akotalwar")] + public async Task RunAsync() + { + string highPrepTimeSumQuery = CreateHighPrepTimeSumQuery(); + string highPrepTimeConditionalQuery = CreateHighPrepTimeConditionalQuery(); + List> globalCustomOdeStatisticsList = new List>(); + + List odeTestCases = new List() + { + //Simple Query + CreateInput("SELECT * FROM c", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT * FROM c", PartitionKeyValue, true, -1, 2500), + + //TOP + CreateInput("SELECT TOP 1000 c.id FROM c", PartitionKeyValue, false, -1, 1000), + CreateInput("SELECT TOP 1000 c.id FROM c", PartitionKeyValue, true, -1, 1000), + + //Filter + CreateInput("SELECT c.id FROM c WHERE c.city IN ('Seattle', 'NYC')", PartitionKeyValue, false, -1, 1250), + CreateInput("SELECT c.id FROM c WHERE c.city IN ('Seattle', 'NYC')", PartitionKeyValue, true, -1, 1250), + + //DISTINCT + Filter + CreateInput("SELECT DISTINCT c.userDefinedId FROM c WHERE c.userDefinedId BETWEEN 0 AND 5 OFFSET 1 LIMIT 3", PartitionKeyValue, false, -1, 3), + CreateInput("SELECT DISTINCT c.userDefinedId FROM c WHERE c.userDefinedId BETWEEN 0 AND 5 OFFSET 1 LIMIT 3", PartitionKeyValue, true, -1, 3), + + CreateInput("SELECT DISTINCT c.city FROM c WHERE STARTSWITH(c.city, 'S')", PartitionKeyValue, false, -1, 2), + CreateInput("SELECT DISTINCT c.city FROM c WHERE STARTSWITH(c.city, 'S')", PartitionKeyValue, true, -1, 2), + + //JOIN + CreateInput("SELECT root.id " + + "FROM root " + + "JOIN root.id a " + + "JOIN root.id b " + + "JOIN root.id c " + + "WHERE root.id = '1' OR a.id in (1,2,3,4,5,6,7,8,9,10) " + + "OR b.id in (1,2,3,4,5,6,7,8,9,10) " + + "OR c.id in (1,2,3,4,5,6,7,8,9,10)", PartitionKeyValue, false, -1, 1), + CreateInput("SELECT root.id " + + "FROM root " + + "JOIN root.id a " + + "JOIN root.id b " + + "JOIN root.id c " + + "WHERE root.id = '1' OR a.id in (1,2,3,4,5,6,7,8,9,10) " + + "OR b.id in (1,2,3,4,5,6,7,8,9,10) " + + "OR c.id in (1,2,3,4,5,6,7,8,9,10)", PartitionKeyValue, true, -1, 1), + + //High Prep Time + CreateInput(highPrepTimeSumQuery, PartitionKeyValue, false, -1, 2500), + CreateInput(highPrepTimeSumQuery, PartitionKeyValue, true, -1, 2500), + + CreateInput(highPrepTimeConditionalQuery, PartitionKeyValue, false, -1, 1750), + CreateInput(highPrepTimeConditionalQuery, PartitionKeyValue, true, -1, 1750), + + //Order By + CreateInput("SELECT * FROM c ORDER BY c.userDefinedId DESC", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT * FROM c ORDER BY c.userDefinedId DESC", PartitionKeyValue, true, -1, 2500), + + CreateInput("SELECT c.id FROM c ORDER BY c.postalcode DESC", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT c.id FROM c ORDER BY c.postalcode DESC", PartitionKeyValue, true, -1, 2500), + + //Order By + TOP + CreateInput("SELECT TOP 5 c.id FROM c ORDER BY c.userDefinedId", PartitionKeyValue, false, -1, 5), + CreateInput("SELECT TOP 5 c.id FROM c ORDER BY c.userDefinedId", PartitionKeyValue, true, -1, 5), + + //Order By + DISTINCT + CreateInput("SELECT DISTINCT c.id FROM c ORDER BY c.city DESC", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT DISTINCT c.id FROM c ORDER BY c.city DESC", PartitionKeyValue, true, -1, 2500), + + //Order By + DISTINCT + Filter + CreateInput("SELECT DISTINCT c.userDefinedId FROM c WHERE c.userDefinedId > 5 ORDER BY c.userDefinedId", PartitionKeyValue, false, -1, 4), + CreateInput("SELECT DISTINCT c.userDefinedId FROM c WHERE c.userDefinedId > 5 ORDER BY c.userDefinedId", PartitionKeyValue, true, -1, 4), + + CreateInput("SELECT DISTINCT c.userDefinedId FROM c WHERE c.userDefinedId BETWEEN 0 AND 5 ORDER BY c.id DESC", PartitionKeyValue, false, -1, 6), + CreateInput("SELECT DISTINCT c.userDefinedId FROM c WHERE c.userDefinedId BETWEEN 0 AND 5 ORDER BY c.id DESC", PartitionKeyValue, true, -1, 6), + + //Group By + CreateInput("SELECT c.postalcode FROM c GROUP BY c.postalcode", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT c.postalcode FROM c GROUP BY c.postalcode", PartitionKeyValue, true, -1, 2500), + + CreateInput("SELECT Count(1) AS count, Sum(ARRAY_LENGTH(c.recipientList)) AS sum FROM c WHERE c.city IN ('Seattle', 'SF') GROUP BY c.city", PartitionKeyValue, false, -1, 2), + CreateInput("SELECT Count(1) AS count, Sum(ARRAY_LENGTH(c.recipientList)) AS sum FROM c WHERE c.city IN ('Seattle', 'SF') GROUP BY c.city", PartitionKeyValue, true, -1, 2), + + CreateInput("SELECT c.city, AVG(ARRAY_LENGTH(c.recipientList)) FROM c GROUP BY c.city", PartitionKeyValue, false, -1, 4), + CreateInput("SELECT c.city, AVG(ARRAY_LENGTH(c.recipientList)) FROM c GROUP BY c.city", PartitionKeyValue, true, -1, 4), + + //Group By + OFFSET + CreateInput("SELECT c.id FROM c GROUP BY c.id OFFSET 5 LIMIT 3", PartitionKeyValue, false, -1, 3), + CreateInput("SELECT c.id FROM c GROUP BY c.id OFFSET 5 LIMIT 3", PartitionKeyValue, true, -1, 3), + + //Group By + TOP + CreateInput("SELECT TOP 25 c.id FROM c GROUP BY c.id", PartitionKeyValue, false, -1, 25), + CreateInput("SELECT TOP 25 c.id FROM c GROUP BY c.id", PartitionKeyValue, true, -1, 25), + + //Group By + DISTINCT + CreateInput("SELECT DISTINCT c.id FROM c GROUP BY c.id", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT DISTINCT c.id FROM c GROUP BY c.id", PartitionKeyValue, true, -1, 2500), + + CreateInput("SELECT DISTINCT c.postalcode FROM c GROUP BY c.postalcode", PartitionKeyValue, false, -1, 2500), + CreateInput("SELECT DISTINCT c.postalcode FROM c GROUP BY c.postalcode", PartitionKeyValue, true, -1, 2500), + }; + + foreach (DirectExecutionTestCase testCase in odeTestCases) + { + globalCustomOdeStatisticsList.AddRange(await this.RunQueryAsync(testCase)); + } + + using (StreamWriter writer = new StreamWriter(new FileStream(RawDataPath, FileMode.Append, FileAccess.Write))) + { + SerializeODEQueryMetrics(writer, globalCustomOdeStatisticsList, NumberOfIterations, rawData: true); + } + + using (StreamWriter writer = new StreamWriter(new FileStream(AggregateDataPath, FileMode.Append, FileAccess.Write))) + { + SerializeODEQueryMetrics(writer, globalCustomOdeStatisticsList, NumberOfIterations, rawData: false); + } + } + + private async Task>> RunQueryAsync(DirectExecutionTestCase queryInput) + { + List> odeQueryStatisticsList = new List>(); + QueryRequestOptions requestOptions = new QueryRequestOptions() + { + MaxItemCount = queryInput.PageSizeOption, + EnableOptimisticDirectExecution = queryInput.EnableOptimisticDirectExecution, + PartitionKey = queryInput.PartitionKey, + }; + + for (int i = 0; i < NumberOfIterations + WarmupIterations; i++) + { + bool isWarmUpIteration = i < WarmupIterations; + using (FeedIterator iterator = this.Container.GetItemQueryIterator( + queryText: queryInput.Query, + requestOptions: requestOptions)) + { + if (isWarmUpIteration) + { + while (iterator.HasMoreResults) + { + await iterator.ReadNextAsync(); + } + } + else + { + odeQueryStatisticsList.Add(await this.GetIteratorStatistics(iterator, queryInput)); + } + } + } + + return odeQueryStatisticsList; + } + + private async Task> GetIteratorStatistics(FeedIterator feedIterator, DirectExecutionTestCase queryInput) + { + MetricsAccumulator metricsAccumulator = new MetricsAccumulator(); + Guid correlatedActivityId = Guid.NewGuid(); + FeedResponse response; + int totalDocumentCount = 0; + string query; + bool enableOde; + List odeQueryStatisticsList = new List(); + + while (feedIterator.HasMoreResults) + { + QueryStatisticsDatumVisitor queryStatisticsDatumVisitor = new QueryStatisticsDatumVisitor(); + System.Diagnostics.Stopwatch totalTime = new System.Diagnostics.Stopwatch(); + System.Diagnostics.Stopwatch traceTime = new System.Diagnostics.Stopwatch(); + + totalTime.Start(); + response = await feedIterator.ReadNextAsync(); + traceTime.Start(); + if (response.RequestCharge != 0) + { + metricsAccumulator.ReadFromTrace(response, queryStatisticsDatumVisitor); + } + + traceTime.Stop(); + totalTime.Stop(); + + query = queryInput.Query; + enableOde = queryInput.EnableOptimisticDirectExecution; + queryStatisticsDatumVisitor.AddEndToEndTime(totalTime.ElapsedMilliseconds - traceTime.ElapsedMilliseconds); + queryStatisticsDatumVisitor.PopulateMetrics(); + + QueryStatisticsMetrics queryStatistics = queryStatisticsDatumVisitor.QueryMetricsList[0]; + queryStatistics.RUCharge = response.RequestCharge; + queryStatistics.CorrelatedActivityId = correlatedActivityId; + + // Each roundtrip is a new item in the list + odeQueryStatisticsList.Add(new OdeQueryStatistics + { + Query = query, + EnableOde = enableOde, + QueryStatisticsMetrics = queryStatistics + }); + + totalDocumentCount += response.Count; + } + + Assert.AreEqual(queryInput.ExpectedResultCount, totalDocumentCount); + + return odeQueryStatisticsList; + } + + private static string CreateHighPrepTimeSumQuery() + { + int exprCount = 9999; + StringBuilder sb = new StringBuilder(); + sb.Append("SELECT r.id FROM root r WHERE "); + for (int i = 0; i < exprCount; i++) + { + sb.Append(i == 0 ? "1" : "+1"); + } + + return sb.Append(" = " + exprCount + " ORDER BY r.id ASC").ToString(); + } + + private static string CreateHighPrepTimeConditionalQuery() + { + int exprCount = 999; + StringBuilder sb = new StringBuilder(); + string[] cityOptions = new string[] { "Seattle", "Chicago", "NYC", "SF" }; + + sb.Append("SELECT * FROM root r WHERE "); + for (int nIdx = 0; nIdx < exprCount; nIdx++) + { + if (nIdx > 0) + { + sb.Append(" OR "); + } + + sb.Append($"r.userDefinedId > {nIdx} AND r.city = '{cityOptions[nIdx % cityOptions.Length]}'"); + } + + return sb.ToString(); + } + + private static void SerializeODEQueryMetrics(TextWriter textWriter, List> customOdeStatisticsList, int numberOfIterations, bool rawData) + { + if (rawData) + { + SerializeODERawDataQueryMetrics(textWriter, customOdeStatisticsList); + } + else + { + SerializeODEProcessedDataQueryMetrics(textWriter, customOdeStatisticsList, numberOfIterations); + } + } + + private static void SerializeODERawDataQueryMetrics(TextWriter textWriter, List> globalOdeQueryStatisticsList) + { + textWriter.WriteLine(); + textWriter.WriteLine(PrintQueryMetrics); + textWriter.Write("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\"", "Query", "ODE", "RUCharge", "BackendTime", "TransitTime", "ClientTime", "EndToEndTime"); + textWriter.WriteLine(); + + foreach (List queryStatisticsList in globalOdeQueryStatisticsList) + { + double totalClientTime = 0; + double totalBackendTime = 0; + double totalEndToEndTime = 0; + double totalTransitTime = 0; + double totalRU = 0; + string query = ""; + bool ode = false; + + foreach (OdeQueryStatistics queryStatistics in queryStatisticsList) + { + QueryStatisticsMetrics metrics = queryStatistics.QueryStatisticsMetrics; + double transitTime = metrics.Created + metrics.ChannelAcquisitionStarted + metrics.Pipelined + metrics.Received + metrics.Completed; + double backendTime = metrics.TotalQueryExecutionTime; + + totalClientTime += metrics.EndToEndTime - (backendTime + transitTime); + totalBackendTime += backendTime; + totalEndToEndTime += metrics.EndToEndTime; + totalTransitTime += transitTime; + totalRU += metrics.RUCharge; + query = queryStatistics.Query; + ode = queryStatistics.EnableOde; + } + + textWriter.WriteLine($"{query},{ode},{totalRU},{totalBackendTime},{totalTransitTime},{totalClientTime},{totalEndToEndTime}"); + } + } + + private static void SerializeODEProcessedDataQueryMetrics(TextWriter textWriter, List> globalOdeQueryStatisticsList, int numberOfIterations) + { + textWriter.WriteLine(); + textWriter.WriteLine(PrintQueryMetrics); + textWriter.Write("\"{0}\",\"{1}\",\"{2}\",\"{3}\"", "Query", "ODE", "RUCharge", "EndToEndTime"); + textWriter.WriteLine(); + + string prevQuery = globalOdeQueryStatisticsList[0][0].Query; + bool prevOde = globalOdeQueryStatisticsList[0][0].EnableOde; + double totalEndToEndTime = 0; + double totalRU = 0; + + foreach (List odeQueryStatisticsList in globalOdeQueryStatisticsList) + { + if (odeQueryStatisticsList[0].Query == prevQuery && odeQueryStatisticsList[0].EnableOde == prevOde) + { + foreach (OdeQueryStatistics odeQueryStatistics in odeQueryStatisticsList) + { + QueryStatisticsMetrics metrics = odeQueryStatistics.QueryStatisticsMetrics; + totalEndToEndTime += metrics.EndToEndTime; + totalRU += metrics.RUCharge; + } + } + else + { + textWriter.WriteLine($"{prevQuery},{prevOde},{totalRU / numberOfIterations},{totalEndToEndTime / numberOfIterations}"); + + foreach (OdeQueryStatistics odeQueryStatistics in odeQueryStatisticsList) + { + QueryStatisticsMetrics metrics = odeQueryStatistics.QueryStatisticsMetrics; + totalEndToEndTime = metrics.EndToEndTime; + totalRU = metrics.RUCharge; + prevQuery = odeQueryStatistics.Query; + prevOde = odeQueryStatistics.EnableOde; + } + } + } + + textWriter.WriteLine($"{prevQuery},{prevOde},{totalRU / numberOfIterations},{totalEndToEndTime / numberOfIterations}"); + } + + private class OdeQueryStatistics + { + public string Query { get; set; } + public bool EnableOde { get; set; } + public QueryStatisticsMetrics QueryStatisticsMetrics { get; set; } + } + + private static DirectExecutionTestCase CreateInput( + string query, + PartitionKey? partitionKey, + bool enableOptimisticDirectExecution, + int pageSizeOption, + int expectedResultCount) + { + return new DirectExecutionTestCase(query, partitionKey, enableOptimisticDirectExecution, pageSizeOption, expectedResultCount); + } + + private readonly struct DirectExecutionTestCase + { + public string Query { get; } + public PartitionKey? PartitionKey { get; } + public bool EnableOptimisticDirectExecution { get; } + public int PageSizeOption { get; } + public int ExpectedResultCount { get; } + + public DirectExecutionTestCase( + string query, + PartitionKey? partitionKey, + bool enableOptimisticDirectExecution, + int pageSizeOption, + int expectedResultCount) + { + this.Query = query; + this.PartitionKey = partitionKey; + this.EnableOptimisticDirectExecution = enableOptimisticDirectExecution; + this.PageSizeOption = pageSizeOption; + this.ExpectedResultCount = expectedResultCount; + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/QueryStatisticsMetrics.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/QueryStatisticsMetrics.cs index 70ed146f4c..d72193fc43 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/QueryStatisticsMetrics.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/QueryPerfTest/QueryStatisticsMetrics.cs @@ -1,7 +1,11 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests { + using System; + internal class QueryStatisticsMetrics { + public Guid CorrelatedActivityId { get; set; } + public double EndToEndTime { get; set; } public double PocoTime { get; set; } @@ -32,6 +36,8 @@ internal class QueryStatisticsMetrics public double Received { get; set; } + public double RUCharge { get; set; } + public double Completed { get; set; } public double BadRequestCreated { get; set; } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/settings.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/settings.json index 51ecc8a707..611ba8c677 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/settings.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/settings.json @@ -11,14 +11,14 @@ "MasterKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", "ServerStalenessIntervalInSeconds": "5", "MasterStalenessIntervalInSeconds": "1", - "ContentSerializationPerformanceTests.CosmosDatabaseId": "db", - "ContentSerializationPerformanceTests.ContainerId": "container", - "ContentSerializationPerformanceTests.ContentSerialization": "JsonText", - "ContentSerializationPerformanceTests.Query": "SELECT TOP 1 c.region FROM c", - "ContentSerializationPerformanceTests.MaxConcurrency": "-1", - "ContentSerializationPerformanceTests.MaxItemCount": "-1", - "ContentSerializationPerformanceTests.NumberOfIterations": "1", - "ContentSerializationPerformanceTests.WarmupIterations": "0", - "ContentSerializationPerformanceTests.UseStronglyTypedIterator": "true" + "QueryPerformanceTests.CosmosDatabaseId": "db", + "QueryPerformanceTests.ContainerId": "container", + "QueryPerformanceTests.ContentSerialization": "JsonText", + "QueryPerformanceTests.Query": "SELECT TOP 1 c.region FROM c", + "QueryPerformanceTests.MaxConcurrency": "-1", + "QueryPerformanceTests.MaxItemCount": "-1", + "QueryPerformanceTests.NumberOfIterations": "1", + "QueryPerformanceTests.WarmupIterations": "0", + "QueryPerformanceTests.UseStronglyTypedIterator": "true" } } \ No newline at end of file From c46614b9311365f203d489394cf475e212ca202f Mon Sep 17 00:00:00 2001 From: akotalwar <94020786+akotalwar@users.noreply.github.com> Date: Sat, 5 Aug 2023 13:44:45 -0700 Subject: [PATCH 05/20] Query: Adds ODE continuation token support for non-ODE pipelines (#4009) * Added code to throw exception if ODE continuation token goes into non ODE pipeline * Removed count variable * Updated test name * Removed ODE continuation token logic from caller class * Simplified code * Fixed comments * Updated continuation token cast * Removed const string for continuation token * Added Ignore flag for test * Added baseline test * Updated baseline test --- .../CosmosQueryExecutionContextFactory.cs | 12 ++++- ...imisticDirectExecutionContinuationToken.cs | 16 +++--- .../CosmosBasicQueryTests.cs | 53 ++++++++++++++++++- ...misticDirectExecutionQueryBaselineTests.cs | 44 +++++++++++++++ 4 files changed, 117 insertions(+), 8 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs index 47aa1c4490..43ebf6a2d8 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs @@ -752,7 +752,17 @@ private static Documents.PartitionKeyDefinition GetPartitionKeyDefinition(InputP ContainerQueryProperties containerQueryProperties, ITrace trace) { - if (!inputParameters.EnableOptimisticDirectExecution) return null; + if (!inputParameters.EnableOptimisticDirectExecution) + { + if (inputParameters.InitialUserContinuationToken != null + && OptimisticDirectExecutionContinuationToken.IsOptimisticDirectExecutionContinuationToken(inputParameters.InitialUserContinuationToken)) + { + throw new MalformedContinuationTokenException($"The continuation token supplied requires the Optimistic Direct Execution flag to be enabled in QueryRequestOptions for the query execution to resume. " + + $"{inputParameters.InitialUserContinuationToken}"); + } + + return null; + } Debug.Assert(containerQueryProperties.ResourceId != null, "CosmosQueryExecutionContextFactory Assert!", "Container ResourceId cannot be null!"); diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/OptimisticDirectExecution/OptimisticDirectExecutionContinuationToken.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/OptimisticDirectExecution/OptimisticDirectExecutionContinuationToken.cs index 59f87295f3..76b2531f01 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/OptimisticDirectExecution/OptimisticDirectExecutionContinuationToken.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/OptimisticDirectExecution/OptimisticDirectExecutionContinuationToken.cs @@ -4,9 +4,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.OptimisticDirectExecutionQuery { - using System; using System.Collections.Generic; - using Microsoft.Azure.Cosmos.ChangeFeed; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Query.Core.Exceptions; using Microsoft.Azure.Cosmos.Query.Core.Monads; @@ -30,6 +28,12 @@ public OptimisticDirectExecutionContinuationToken(ParallelContinuationToken toke public Range Range => this.Token.Range; + public static bool IsOptimisticDirectExecutionContinuationToken(CosmosElement continuationToken) + { + CosmosObject cosmosObjectContinuationToken = continuationToken as CosmosObject; + return !(cosmosObjectContinuationToken == null || !cosmosObjectContinuationToken.ContainsKey(OptimisticDirectExecutionToken)); + } + public static CosmosElement ToCosmosElement(OptimisticDirectExecutionContinuationToken continuationToken) { CosmosElement inner = ParallelContinuationToken.ToCosmosElement(continuationToken.Token); @@ -42,14 +46,14 @@ public static CosmosElement ToCosmosElement(OptimisticDirectExecutionContinuatio public static TryCatch TryCreateFromCosmosElement(CosmosElement cosmosElement) { - CosmosObject cosmosObjectContinuationToken = cosmosElement as CosmosObject; - if (cosmosObjectContinuationToken == null || !cosmosObjectContinuationToken.ContainsKey(OptimisticDirectExecutionToken)) + if (!IsOptimisticDirectExecutionContinuationToken(cosmosElement)) { return TryCatch.FromException( - new MalformedContinuationTokenException( - message: $"Malformed Continuation Token: Expected OptimisticDirectExecutionToken\r\n")); + new MalformedContinuationTokenException( + message: $"Malformed Continuation Token: Expected OptimisticDirectExecutionToken\r\n")); } + CosmosObject cosmosObjectContinuationToken = (CosmosObject)cosmosElement; TryCatch inner = ParallelContinuationToken.TryCreateFromCosmosElement(cosmosObjectContinuationToken[OptimisticDirectExecutionToken]); return inner.Succeeded ? diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs index 25ac9a705f..bfd6366104 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs @@ -15,7 +15,6 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using Cosmos.Scripts; using Microsoft.Azure.Cosmos.Fluent; using Microsoft.Azure.Cosmos.Linq; - using Microsoft.Azure.Cosmos.Query.Core; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents.Collections; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -786,6 +785,58 @@ public async Task QueryActivityIdWithContinuationTokenAndTraceTest() } + //TODO: Remove Ignore flag once emulator is updated to 0415 + [Ignore] + [TestMethod] + public async Task TesOdeTokenCompatibilityWithNonOdePipeline() + { + string query = "select top 200 * from c"; + CosmosClient client = DirectCosmosClient; + Container container = client.GetContainer(DatabaseId, ContainerId); + + // Create items + for (int i = 0; i < 500; i++) + { + await container.CreateItemAsync(ToDoActivity.CreateRandomToDoActivity()); + } + + QueryRequestOptions queryRequestOptions = new QueryRequestOptions + { + MaxItemCount = 50, + EnableOptimisticDirectExecution = true + }; + + FeedIteratorInternal feedIterator = + (FeedIteratorInternal)container.GetItemQueryStreamIterator( + query, + null, + queryRequestOptions); + + ResponseMessage responseMessage = await feedIterator.ReadNextAsync(CancellationToken.None); + string continuationToken = responseMessage.ContinuationToken; + + QueryRequestOptions newQueryRequestOptions = new QueryRequestOptions + { + MaxItemCount = 50, + EnableOptimisticDirectExecution = false + }; + + // use Continuation Token to create new iterator and use same trace + FeedIterator feedIteratorNew = + container.GetItemQueryStreamIterator( + query, + continuationToken, + newQueryRequestOptions); + + while (feedIteratorNew.HasMoreResults) + { + responseMessage = await feedIteratorNew.ReadNextAsync(CancellationToken.None); + } + + string expectedErrorMessage = "The continuation token supplied requires the Optimistic Direct Execution flag to be enabled in QueryRequestOptions for the query execution to resume. "; + Assert.IsTrue(responseMessage.CosmosException.ToString().Contains(expectedErrorMessage)); + } + private class CustomHandler : RequestHandler { string correlatedActivityId; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs index 81c765adc4..5c0e66f1a6 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs @@ -19,6 +19,7 @@ using Microsoft.Azure.Cosmos.Query.Core.Pipeline; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.Parallel; + using Microsoft.Azure.Cosmos.Query.Core.Pipeline.OptimisticDirectExecutionQuery; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Pagination; using Microsoft.Azure.Cosmos.Query.Core.QueryClient; using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; @@ -190,6 +191,49 @@ public async Task TestPipelineForBackendDocumentsOnSinglePartitionAsync() Assert.AreEqual(100, documentCountInSinglePartition); } + [TestMethod] + public async Task TestOdeTokenWithSpecializedPipeline() + { + int numItems = 100; + ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken( + token: Guid.NewGuid().ToString(), + range: new Documents.Routing.Range("A", "B", true, false)); + + OptimisticDirectExecutionContinuationToken optimisticDirectExecutionContinuationToken = new OptimisticDirectExecutionContinuationToken(parallelContinuationToken); + CosmosElement cosmosElementContinuationToken = OptimisticDirectExecutionContinuationToken.ToCosmosElement(optimisticDirectExecutionContinuationToken); + + OptimisticDirectExecutionTestInput input = CreateInput( + description: @"Single Partition Key and Value Field", + query: "SELECT VALUE COUNT(1) FROM c", + expectedOptimisticDirectExecution: false, + partitionKeyPath: @"/pk", + partitionKeyValue: "a", + continuationToken: cosmosElementContinuationToken); + + DocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems, multiPartition: false); + QueryRequestOptions queryRequestOptions = GetQueryRequestOptions(enableOptimisticDirectExecution: input.ExpectedOptimisticDirectExecution); + (CosmosQueryExecutionContextFactory.InputParameters inputParameters, CosmosQueryContextCore cosmosQueryContextCore) = CreateInputParamsAndQueryContext(input, queryRequestOptions); + IQueryPipelineStage queryPipelineStage = CosmosQueryExecutionContextFactory.Create( + documentContainer, + cosmosQueryContextCore, + inputParameters, + NoOpTrace.Singleton); + + string expectedErrorMessage = "The continuation token supplied requires the Optimistic Direct Execution flag to be enabled in QueryRequestOptions for the query execution to resume. "; + + while (await queryPipelineStage.MoveNextAsync(NoOpTrace.Singleton)) + { + if (queryPipelineStage.Current.Failed) + { + Assert.IsTrue(queryPipelineStage.Current.InnerMostException.ToString().Contains(expectedErrorMessage)); + return; + } + + Assert.IsFalse(true); + break; + } + } + [TestMethod] public async Task TestQueriesWhichNeverRequireDistribution() { From e05e3eb23c4afd241dc24f3a8872f3ab5b9f431f Mon Sep 17 00:00:00 2001 From: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:39:37 -0700 Subject: [PATCH 06/20] Code changes to disable replica validation in preview package. (#4019) --- Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs index 216e1295b5..748f81833a 100644 --- a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs +++ b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs @@ -38,9 +38,7 @@ public static bool IsReplicaAddressValidationEnabled( ConnectionPolicy connectionPolicy) { bool replicaValidationDefaultValue = false; -#if PREVIEW - replicaValidationDefaultValue = true; -#endif + if (connectionPolicy != null && connectionPolicy.EnableAdvancedReplicaSelectionForTcp.HasValue) { From 27b935072764a901b3eed4b038aff7956f2d297e Mon Sep 17 00:00:00 2001 From: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com> Date: Tue, 8 Aug 2023 21:08:02 -0700 Subject: [PATCH 07/20] 3.35.3: Adds new SDK versions and contract files (#4024) * Updated change log and bumped up the patch version. * Code changes to update the change log message. --- Directory.Build.props | 4 +- .../contracts/API_3.35.3-preview.txt | 1563 +++++++++++++++++ .../contracts/API_3.35.3.txt | 1502 ++++++++++++++++ changelog.md | 9 + 4 files changed, 3076 insertions(+), 2 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/contracts/API_3.35.3-preview.txt create mode 100644 Microsoft.Azure.Cosmos/contracts/API_3.35.3.txt diff --git a/Directory.Build.props b/Directory.Build.props index 9daa1e7e9f..d3ceab0046 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.35.2 - 3.35.2 + 3.35.3 + 3.35.3 preview 3.31.3 2.0.3 diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.35.3-preview.txt b/Microsoft.Azure.Cosmos/contracts/API_3.35.3-preview.txt new file mode 100644 index 0000000000..6c989053fd --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.35.3-preview.txt @@ -0,0 +1,1563 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public class ChangeFeedItemChange + { + public ChangeFeedItemChange(); + public T Current { get; set; } + public ChangeFeedMetadata Metadata { get; set; } + public T Previous { get; set; } + } + public class ChangeFeedMetadata + { + public ChangeFeedMetadata(DateTime conflictResolutionTimestamp, long lsn, ChangeFeedOperationType operationType, long previousLsn); + public DateTime ConflictResolutionTimestamp { get; } + public bool IsTimeToLiveExpired { get; } + public long Lsn { get; } + public ChangeFeedOperationType OperationType { get; } + public long PreviousLsn { get; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode AllVersionsAndDeletes { get; } + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public enum ChangeFeedOperationType + { + Create = 0, + Delete = 2, + Replace = 1, + } + public sealed class ChangeFeedPolicy + { + public ChangeFeedPolicy(); + public static TimeSpan FullFidelityNoRetention { get; } + public TimeSpan FullFidelityRetention { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public sealed class ComputedProperty + { + public ComputedProperty(); + public string Name { get; set; } + public string Query { get; set; } + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public abstract Task DeleteAllItemsByPartitionKeyStreamAsync(PartitionKey partitionKey, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> GetPartitionKeyRangesAsync(FeedRange feedRange, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ChangeFeedPolicy ChangeFeedPolicy { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public Collection ComputedProperties { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool IsDistributedTracingEnabled { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public IWebProxy WebProxy { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable BypassIntegratedCache { get; set; } + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public enum PriorityLevel + { + High = 1, + Low = 2, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public bool EnableOptimisticDirectExecution { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyCentral = "Germany Central"; + public const string GermanyNorth = "Germany North"; + public const string GermanyNortheast = "Germany Northeast"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IsraelCentral = "Israel Central"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SouthIndia = "South India"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public Nullable PriorityLevel { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class ChangeFeedPolicyDefinition + { + public ContainerBuilder Attach(); + } + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ComputedPropertiesDefinition + { + public T Attach(); + public ComputedPropertiesDefinition WithComputedProperty(string name, string query); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ChangeFeedPolicyDefinition WithChangeFeedPolicy(TimeSpan retention); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public UniqueKeyDefinition WithUniqueKey(); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public ComputedPropertiesDefinition WithComputedProperties(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithDistributedTracing(bool isEnabled=true); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query, IDictionary namedParameters); + public static FeedIterator ToStreamIterator(this IQueryable query); + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.35.3.txt b/Microsoft.Azure.Cosmos/contracts/API_3.35.3.txt new file mode 100644 index 0000000000..c0dc3e280f --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.35.3.txt @@ -0,0 +1,1502 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedEstimator + { + protected ChangeFeedEstimator(); + public abstract FeedIterator GetCurrentStateIterator(ChangeFeedEstimatorRequestOptions changeFeedEstimatorRequestOptions=null); + } + public sealed class ChangeFeedEstimatorRequestOptions + { + public ChangeFeedEstimatorRequestOptions(); + public Nullable MaxItemCount { get; set; } + } + public abstract class ChangeFeedMode + { + public static ChangeFeedMode Incremental { get; } + public static ChangeFeedMode LatestVersion { get; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithLeaseReleaseNotification(Container.ChangeFeedMonitorLeaseReleaseDelegate releaseDelegate); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public abstract class ChangeFeedProcessorContext + { + protected ChangeFeedProcessorContext(); + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract Headers Headers { get; } + public abstract string LeaseToken { get; } + } + public sealed class ChangeFeedProcessorState + { + public ChangeFeedProcessorState(string leaseToken, long estimatedLag, string instanceName); + public long EstimatedLag { get; } + public string InstanceName { get; } + public string LeaseToken { get; } + } + public class ChangeFeedProcessorUserException : Exception + { + public ChangeFeedProcessorUserException(Exception originalException, ChangeFeedProcessorContext context); + protected ChangeFeedProcessorUserException(SerializationInfo info, StreamingContext context); + public ChangeFeedProcessorContext ChangeFeedProcessorContext { get; } + public override void GetObjectData(SerializationInfo info, StreamingContext context); + } + public sealed class ChangeFeedRequestOptions : RequestOptions + { + public ChangeFeedRequestOptions(); + public new string IfMatchEtag { get; set; } + public new string IfNoneMatchEtag { get; set; } + public Nullable PageSizeHint { get; set; } + } + public abstract class ChangeFeedStartFrom + { + public static ChangeFeedStartFrom Beginning(); + public static ChangeFeedStartFrom Beginning(FeedRange feedRange); + public static ChangeFeedStartFrom ContinuationToken(string continuationToken); + public static ChangeFeedStartFrom Now(); + public static ChangeFeedStartFrom Now(FeedRange feedRange); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc); + public static ChangeFeedStartFrom Time(DateTime dateTimeUtc, FeedRange feedRange); + } + public sealed class ClientEncryptionIncludedPath + { + public ClientEncryptionIncludedPath(); + public string ClientEncryptionKeyId { get; set; } + public string EncryptionAlgorithm { get; set; } + public string EncryptionType { get; set; } + public string Path { get; set; } + } + public abstract class ClientEncryptionKey + { + protected ClientEncryptionKey(); + public abstract string Id { get; } + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class ClientEncryptionKeyProperties : IEquatable + { + protected ClientEncryptionKeyProperties(); + public ClientEncryptionKeyProperties(string id, string encryptionAlgorithm, byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata); + public Nullable CreatedTime { get; } + public string EncryptionAlgorithm { get; } + public EncryptionKeyWrapMetadata EncryptionKeyWrapMetadata { get; } + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public virtual string SelfLink { get; } + public byte[] WrappedDataEncryptionKey { get; } + public bool Equals(ClientEncryptionKeyProperties other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public class ClientEncryptionKeyResponse : Response + { + protected ClientEncryptionKeyResponse(); + public override string ActivityId { get; } + public virtual ClientEncryptionKey ClientEncryptionKey { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ClientEncryptionKeyProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ClientEncryptionKey (ClientEncryptionKeyResponse response); + } + public sealed class ClientEncryptionPolicy + { + public ClientEncryptionPolicy(IEnumerable includedPaths); + public ClientEncryptionPolicy(IEnumerable includedPaths, int policyFormatVersion); + public IEnumerable IncludedPaths { get; } + public int PolicyFormatVersion { get; } + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedEstimator GetChangeFeedEstimator(string processorName, Container leaseContainer); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract FeedIterator GetChangeFeedIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedStreamHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedStreamHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilderWithManualCheckpoint(string processorName, Container.ChangeFeedHandlerWithManualCheckpoint onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangeFeedHandler onChangesDelegate); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract FeedIterator GetChangeFeedStreamIterator(ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedMode changeFeedMode, ChangeFeedRequestOptions changeFeedRequestOptions=null); + public abstract Task> GetFeedRangesAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null, CosmosLinqSerializerOptions linqSerializerOptions=null); + public abstract FeedIterator GetItemQueryIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(FeedRange feedRange, QueryDefinition queryDefinition, string continuationToken, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task> PatchItemAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task PatchItemStreamAsync(string id, PartitionKey partitionKey, IReadOnlyList patchOperations, PatchItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadManyItemsAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadManyItemsStreamAsync(IReadOnlyList> items, ReadManyRequestOptions readManyRequestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangeFeedHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, IReadOnlyCollection changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangeFeedHandler(ChangeFeedProcessorContext context, IReadOnlyCollection changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedMonitorErrorDelegate(string leaseToken, Exception exception); + public delegate Task ChangeFeedMonitorLeaseAcquireDelegate(string leaseToken); + public delegate Task ChangeFeedMonitorLeaseReleaseDelegate(string leaseToken); + public delegate Task ChangeFeedStreamHandler(ChangeFeedProcessorContext context, Stream changes, CancellationToken cancellationToken); + public delegate Task ChangeFeedStreamHandlerWithManualCheckpoint(ChangeFeedProcessorContext context, Stream changes, Func checkpointAsync, CancellationToken cancellationToken); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, IReadOnlyList partitionKeyPaths); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ClientEncryptionPolicy ClientEncryptionPolicy { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public IReadOnlyList PartitionKeyPaths { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions=null); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual CosmosResponseFactory ResponseFactory { get; } + public static Task CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, TokenCredential tokenCredential, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string connectionString, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public static Task CreateAndInitializeAsync(string accountEndpoint, string authKeyOrResourceToken, IReadOnlyList> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public Collection CustomHandlers { get; } + public Nullable EnableContentResponseOnWrite { get; set; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public Func ServerCertificateCustomValidationCallback { get; set; } + public Nullable TokenCredentialBackgroundRefreshInterval { get; set; } + public IWebProxy WebProxy { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract IReadOnlyList> GetContactedRegions(); + public virtual int GetFailedRequestCount(); + public virtual Nullable GetStartTimeUtc(); + public abstract override string ToString(); + } + public class CosmosException : Exception + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public override string Message { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public sealed class CosmosLinqSerializerOptions + { + public CosmosLinqSerializerOptions(); + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public class CosmosOperationCanceledException : OperationCanceledException + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public override Exception GetBaseException(); + public override void GetObjectData(SerializationInfo info, StreamingContext context); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public abstract class CosmosResponseFactory + { + protected CosmosResponseFactory(); + public abstract FeedResponse CreateItemFeedResponse(ResponseMessage responseMessage); + public abstract ItemResponse CreateItemResponse(ResponseMessage responseMessage); + public abstract StoredProcedureExecuteResponse CreateStoredProcedureExecuteResponse(ResponseMessage responseMessage); + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateClientEncryptionKeyAsync(ClientEncryptionKeyProperties clientEncryptionKeyProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ClientEncryptionKey GetClientEncryptionKey(string id); + public abstract FeedIterator GetClientEncryptionKeyQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public class DedicatedGatewayRequestOptions + { + public DedicatedGatewayRequestOptions(); + public Nullable MaxIntegratedCacheStaleness { get; set; } + } + public class EncryptionKeyWrapMetadata : IEquatable + { + public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source); + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm); + public string Algorithm { get; } + public string Name { get; } + public string Type { get; } + public string Value { get; } + public bool Equals(EncryptionKeyWrapMetadata other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedRange + { + protected FeedRange(); + public static FeedRange FromJsonString(string toStringValue); + public static FeedRange FromPartitionKey(PartitionKey partitionKey); + public abstract string ToJsonString(); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public abstract string IndexMetrics { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public sealed class PartitionKeyBuilder + { + public PartitionKeyBuilder(); + public PartitionKeyBuilder Add(bool val); + public PartitionKeyBuilder Add(double val); + public PartitionKeyBuilder Add(string val); + public PartitionKeyBuilder AddNoneType(); + public PartitionKeyBuilder AddNullValue(); + public PartitionKey Build(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public sealed class PatchItemRequestOptions : ItemRequestOptions + { + public PatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public abstract class PatchOperation + { + protected PatchOperation(); + public virtual string From { get; set; } + public abstract PatchOperationType OperationType { get; } + public abstract string Path { get; } + public static PatchOperation Add(string path, T value); + public static PatchOperation Increment(string path, double value); + public static PatchOperation Increment(string path, long value); + public static PatchOperation Move(string from, string path); + public static PatchOperation Remove(string path); + public static PatchOperation Replace(string path, T value); + public static PatchOperation Set(string path, T value); + public virtual bool TrySerializeValueParameter(CosmosSerializer cosmosSerializer, out Stream valueParam); + } + public enum PatchOperationType + { + Add = 0, + Increment = 4, + Move = 5, + Remove = 1, + Replace = 2, + Set = 3, + } + public abstract class PatchOperation : PatchOperation + { + protected PatchOperation(); + public abstract T Value { get; } + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public IReadOnlyList> GetQueryParameters(); + public QueryDefinition WithParameter(string name, object value); + public QueryDefinition WithParameterStream(string name, Stream valueStream); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable PopulateIndexMetrics { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public class ReadManyRequestOptions : RequestOptions + { + public ReadManyRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaEast3 = "China East 3"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string ChinaNorth3 = "China North 3"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string EastUSSLV = "East US SLV"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyCentral = "Germany Central"; + public const string GermanyNorth = "Germany North"; + public const string GermanyNortheast = "Germany Northeast"; + public const string GermanyWestCentral = "Germany West Central"; + public const string IsraelCentral = "Israel Central"; + public const string ItalyNorth = "Italy North"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string MalaysiaSouth = "Malaysia South"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string PolandCentral = "Poland Central"; + public const string QatarCentral = "Qatar Central"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SouthIndia = "South India"; + public const string SwedenCentral = "Sweden Central"; + public const string SwedenSouth = "Sweden South"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public Action AddRequestHeaders { get; set; } + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public IReadOnlyDictionary Properties { get; set; } + public RequestOptions ShallowCopy(); + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; set; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public string IndexMetrics { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch PatchItem(string id, IReadOnlyList patchOperations, TransactionalBatchPatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchPatchItemRequestOptions : TransactionalBatchItemRequestOptions + { + public TransactionalBatchPatchItemRequestOptions(); + public string FilterPredicate { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public string SessionToken { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public sealed class ClientEncryptionPolicyDefinition + { + public ContainerBuilder Attach(); + public ClientEncryptionPolicyDefinition WithIncludedPath(ClientEncryptionIncludedPath path); + } + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public ContainerBuilder(Database database, string name, string partitionKeyPath); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(); + public ClientEncryptionPolicyDefinition WithClientEncryptionPolicy(int policyFormatVersion); + public ConflictResolutionDefinition WithConflictResolution(); + public UniqueKeyDefinition WithUniqueKey(); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential); + public CosmosClientBuilder(string accountEndpoint, TokenCredential tokenCredential); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public Task BuildAndInitializeAsync(IReadOnlyList> containers, CancellationToken cancellationToken=default(CancellationToken)); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinq + { + public static object InvokeUserDefinedFunction(string udfName, params object[] arguments); + } + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static bool IsArray(this object obj); + public static bool IsBool(this object obj); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsNumber(this object obj); + public static bool IsObject(this object obj); + public static bool IsPrimitive(this object obj); + public static bool IsString(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/changelog.md b/changelog.md index a6f9e65864..f7a46f39a3 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,15 @@ Preview features are treated as a separate branch and will not be included in th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +### [3.35.3-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.3-preview) - 2023-08-09 +### [3.35.3](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.3) - 2023-08-09 + +#### Fixed +- [3979](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3979) Client Encryption: Adds fix for supporting Prefix Partition Key (Hierarchical partitioning) + +#### Added +- [4019](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/4019) Upgrade Resiliency: Disables Replica Validation Feature By Default in Preview (The feature was previously enabled by default in the [`3.35.2-preview`](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.2-preview) release) + ### [3.35.2-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.2-preview) - 2023-07-17 #### Fixed From f8f38017f091bf9691db52724f153e3f338d72d5 Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Thu, 10 Aug 2023 03:05:06 +0530 Subject: [PATCH 08/20] [Internal] Distributed tracing: Adds a sample to collect activities and events using custom listener (#4021) * custom listener example * removed unwanted code * add comments * fix appsettings * revert changes --- .../Usage/Cosmos.Samples.Usage.sln | 8 +- .../CustomDiagnosticAndEventListener.cs | 167 ++++++++++++++++++ .../CustomDiagnosticAndEventListener.csproj | 20 +++ .../Program.cs | 90 ++++++++++ 4 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.cs create mode 100644 Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.csproj create mode 100644 Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/Program.cs diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/Cosmos.Samples.Usage.sln b/Microsoft.Azure.Cosmos.Samples/Usage/Cosmos.Samples.Usage.sln index 999b48d928..5e23b5c075 100644 --- a/Microsoft.Azure.Cosmos.Samples/Usage/Cosmos.Samples.Usage.sln +++ b/Microsoft.Azure.Cosmos.Samples/Usage/Cosmos.Samples.Usage.sln @@ -53,7 +53,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CFPullModelLatestVersionMod EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry", "OpenTelemetry\OpenTelemetry.csproj", "{C6EF6948-C085-4013-A21F-99303ECBA7A9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationInsights", "ApplicationInsights\ApplicationInsights.csproj", "{55149A3C-A263-4EE5-AD2D-02FE9AC4D291}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationInsights", "ApplicationInsights\ApplicationInsights.csproj", "{55149A3C-A263-4EE5-AD2D-02FE9AC4D291}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomDiagnosticAndEventListener", "CustomDiagnosticAndEventListener\CustomDiagnosticAndEventListener.csproj", "{9BE3551E-31A1-4186-9D2F-DC325411A39D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -165,6 +167,10 @@ Global {55149A3C-A263-4EE5-AD2D-02FE9AC4D291}.Debug|Any CPU.Build.0 = Debug|Any CPU {55149A3C-A263-4EE5-AD2D-02FE9AC4D291}.Release|Any CPU.ActiveCfg = Release|Any CPU {55149A3C-A263-4EE5-AD2D-02FE9AC4D291}.Release|Any CPU.Build.0 = Release|Any CPU + {9BE3551E-31A1-4186-9D2F-DC325411A39D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BE3551E-31A1-4186-9D2F-DC325411A39D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BE3551E-31A1-4186-9D2F-DC325411A39D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BE3551E-31A1-4186-9D2F-DC325411A39D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.cs b/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.cs new file mode 100644 index 0000000000..17348d9a22 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.cs @@ -0,0 +1,167 @@ +namespace Sample.Listeners +{ + using System.Diagnostics.Tracing; + using System.Diagnostics; + using System.Collections.Concurrent; + + /// + /// This listener can cover following aspects: + /// 1. Write its own monitoring library with the custom implementation of aggregation or whatever you want to do with this data. + /// 2. Support an APM tool which is not open telemetry compliant. + /// + /// It is a simple sample. Anybody can get as creative as they want to make it better in terms of usability and performance. + internal class CustomDiagnosticAndEventListener : + EventListener, // Override Event Listener to capture Event source events + IObserver>, // Override IObserver to capture Activity events + IObserver, + IDisposable + { + private readonly string diagnosticSourceName; + private readonly string eventSourceName; + + private ConcurrentBag? Subscriptions = new(); + private ConcurrentBag Activities { get; } = new(); + + public CustomDiagnosticAndEventListener(string diagnosticSourceName, string eventSourceName) + { + this.diagnosticSourceName = diagnosticSourceName; + this.eventSourceName = eventSourceName; + + DiagnosticListener.AllListeners.Subscribe(this); + } + + /// + /// IObserver Override + /// + public void OnCompleted() { + Console.WriteLine("OnCompleted"); + } + + /// + /// IObserver Override + /// + public void OnError(Exception error) { + Console.WriteLine($"OnError : {error}"); + } + + /// + /// IObserver Override + /// + public void OnNext(KeyValuePair value) + { + lock (this.Activities) + { + // Check for disposal + if (this.Subscriptions == null) return; + + string startSuffix = ".Start"; + string stopSuffix = ".Stop"; + string exceptionSuffix = ".Exception"; + + if (Activity.Current == null) + { + return; + } + + if (value.Key.EndsWith(startSuffix)) + { + this.Activities.Add(Activity.Current); + } + else if (value.Key.EndsWith(stopSuffix) || value.Key.EndsWith(exceptionSuffix)) + { + foreach (Activity activity in this.Activities) + { + if (activity.Id == Activity.Current.Id) + { + Console.WriteLine($" Activity Name: {activity.DisplayName}"); + Console.WriteLine($" Activity Operation Name: {activity.OperationName}"); + foreach (KeyValuePair actualTag in activity.Tags) + { + Console.WriteLine($" {actualTag.Key} ==> {actualTag.Value}"); + } + Console.WriteLine(); + return; + } + } + } + } + } + + /// + /// IObserver Override + /// + public void OnNext(DiagnosticListener value) + { + if (value.Name == this.diagnosticSourceName && this.Subscriptions != null) + { + Console.WriteLine($"CustomDiagnosticAndEventListener : OnNext : {value.Name}"); + lock (this.Activities) + { + this.Subscriptions?.Add(value.Subscribe(this)); + } + } + } + + /// + /// EventListener Override + /// + protected override void OnEventSourceCreated(EventSource eventSource) + { + if (eventSource != null && eventSource.Name.Equals(this.eventSourceName)) + { + Console.WriteLine($"CustomDiagnosticAndEventListener : OnEventSourceCreated : {eventSource.Name}"); + this.EnableEvents(eventSource, EventLevel.Informational); // Enable information level events + } + } + + /// + /// EventListener Override + /// + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + Console.WriteLine($" Event Name: {eventData.EventName}"); + Console.WriteLine($" Event Level: {eventData.Level}"); + if(eventData.Payload != null) + { + int counter = 0; + foreach (object? payload in eventData.Payload) + { + Console.WriteLine($" Event Payload {counter++}: {payload}"); + } + } + else + { + Console.WriteLine($" Event Payload: NULL"); + } + Console.WriteLine(); + } + + public override void Dispose() + { + Console.WriteLine("CustomDiagnosticAndEventListener : Dispose"); + base.Dispose(); + + if (this.Subscriptions == null) + { + return; + } + + ConcurrentBag subscriptions; + lock (this.Activities) + { + subscriptions = this.Subscriptions; + this.Subscriptions = null; + } + + foreach (IDisposable subscription in subscriptions) + { + subscription.Dispose(); // Dispose of DiagnosticListener subscription + } + + foreach (Activity activity in this.Activities) + { + activity.Dispose(); // Dispose of Activity + } + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.csproj b/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.csproj new file mode 100644 index 0000000000..eeb17eb145 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/CustomDiagnosticAndEventListener.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + PreserveNewest + + + diff --git a/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/Program.cs b/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/Program.cs new file mode 100644 index 0000000000..77580936f7 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Usage/CustomDiagnosticAndEventListener/Program.cs @@ -0,0 +1,90 @@ +namespace Cosmos.Samples.ApplicationInsights +{ + using Microsoft.Azure.Cosmos; + using Microsoft.Extensions.Configuration; + using Newtonsoft.Json; + using Sample.Listeners; + + internal class Program + { + private static readonly string databaseName = "samples"; + private static readonly string containerName = "custom-listener-sample"; + + static async Task Main() + { + IConfigurationRoot configuration = new ConfigurationBuilder() + .AddJsonFile("AppSettings.json") + .Build(); + + string endpoint = configuration["EndPointUrl"]; + if (string.IsNullOrEmpty(endpoint)) + { + throw new ArgumentNullException("Please specify a valid CosmosDBEndPointUrl in the appSettings.json"); + } + + string authKey = configuration["AuthorizationKey"]; + if (string.IsNullOrEmpty(authKey) || string.Equals(authKey, "Super secret key")) + { + throw new ArgumentException("Please specify a valid CosmosDBAuthorizationKey in the appSettings.json"); + } + + using CustomDiagnosticAndEventListener listener + = new CustomDiagnosticAndEventListener( + diagnosticSourceName: "Azure.Cosmos.Operation", + eventSourceName: "Azure-Cosmos-Operation-Request-Diagnostics"); + + CosmosClientOptions options = new CosmosClientOptions() + { + IsDistributedTracingEnabled = true // Defaults to true, set to false to disable + }; + using (CosmosClient client = new CosmosClient(endpoint, authKey, options)) + { + Console.WriteLine($"Getting container reference for {containerName}."); + + ContainerProperties properties = new ContainerProperties(containerName, partitionKeyPath: "/id"); + + await client.CreateDatabaseIfNotExistsAsync(databaseName); + Container container = await client.GetDatabase(databaseName).CreateContainerIfNotExistsAsync(properties); + + await Program.RunCrudDemo(container); + } + } + + public static async Task RunCrudDemo(Container container) + { + // Any operations will automatically generate telemetry + + for (int i = 1; i <= 5; i++) + { + await container.CreateItemAsync(new Item { Id = $"{i}", Status = "new" }, new PartitionKey($"{i}")); + Console.WriteLine($"Created document with id: {i}"); + } + + for (int i = 1; i <= 5; i++) + { + await container.ReadItemAsync($"{i}", new PartitionKey($"{i}")); + Console.WriteLine($"Read document with id: {i}"); + } + + for (int i = 1; i <= 5; i++) + { + await container.ReplaceItemAsync(new Item { Id = $"{i}", Status = "updated" }, $"{i}", new PartitionKey($"{i}")); + Console.WriteLine($"Updated document with id: {i}"); + } + + for (int i = 1; i <= 5; i++) + { + await container.DeleteItemAsync($"{i}", new PartitionKey($"{i}")); + Console.WriteLine($"Deleted document with id: {i}"); + } + } + } + + internal class Item + { + [JsonProperty("id")] + public string Id { get; set; } + + public string Status { get; set; } + } +} \ No newline at end of file From 32827f7846efdfa58be1a72cfe5e0e13cc3fe485 Mon Sep 17 00:00:00 2001 From: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com> Date: Thu, 10 Aug 2023 12:59:38 -0700 Subject: [PATCH 09/20] Code changes to fix race condition by calling dispose too early. (#4030) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index d3ceab0046..044a7ec7d9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ 3.35.3 3.35.3 preview - 3.31.3 + 3.31.4 2.0.3 2.0.3 preview From efb90f094166ab1c9ab8cd8ef84b556ec6c887e0 Mon Sep 17 00:00:00 2001 From: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:25:15 -0700 Subject: [PATCH 10/20] Code changes to update change log for release 3.35.3 (#4032) --- changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index f7a46f39a3..27ec98fae0 100644 --- a/changelog.md +++ b/changelog.md @@ -13,11 +13,11 @@ Preview features are treated as a separate branch and will not be included in th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -### [3.35.3-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.3-preview) - 2023-08-09 -### [3.35.3](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.3) - 2023-08-09 +### [3.35.3-preview](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.3-preview) - 2023-08-10 +### [3.35.3](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.3) - 2023-08-10 #### Fixed -- [3979](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3979) Client Encryption: Adds fix for supporting Prefix Partition Key (Hierarchical partitioning) +- [4030](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/4030) Upgrade Resiliency: Fixes Race Condition by Calling Dispose Too Early #### Added - [4019](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/4019) Upgrade Resiliency: Disables Replica Validation Feature By Default in Preview (The feature was previously enabled by default in the [`3.35.2-preview`](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.35.2-preview) release) From e708ec9025626121ef9e07cf9146a71daefc296e Mon Sep 17 00:00:00 2001 From: Rinat Minibaev <132935507+rinatmini@users.noreply.github.com> Date: Thu, 10 Aug 2023 18:14:47 -0700 Subject: [PATCH 11/20] Documentation: Fixes article links (replaced links V2 to V3 SDK version) + Azure Cosmos DB typo (#4031) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Documentation link fix * Fixed Typo "Azure CosmosDB"→"Azure Cosmos DB" --- .../src/ConnectionPolicy.cs | 6 ++--- Microsoft.Azure.Cosmos/src/CosmosClient.cs | 26 +++++++++---------- .../src/CosmosClientOptions.cs | 6 ++--- .../src/Diagnostics/CosmosDiagnostics.cs | 4 +-- .../src/Fluent/CosmosClientBuilder.cs | 8 +++--- .../DedicatedGatewayRequestOptions.cs | 4 +-- Microsoft.Azure.Cosmos/src/RetryOptions.cs | 4 +-- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs b/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs index 9193abedef..c8c1d9db93 100644 --- a/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs @@ -131,7 +131,7 @@ public TimeSpan MediaRequestTimeout /// Default value is /// /// - /// For more information, see Connection policy: Use direct connection mode. + /// For more information, see Connection policy: Use direct connection mode. /// public ConnectionMode ConnectionMode { @@ -160,7 +160,7 @@ public MediaReadMode MediaReadMode /// /// This setting is not used when is set to . /// Gateway mode only supports HTTPS. - /// For more information, see Connection policy: Use the TCP protocol. + /// For more information, see Connection policy: Use the HTTPS protocol. /// public Protocol ConnectionProtocol { @@ -369,7 +369,7 @@ public int MaxConnectionLimit /// set to 9 and set to 30 seconds. /// /// - /// For more information, see Handle rate limiting/request rate too large. + /// For more information, see Handle rate limiting/request rate too large. /// public RetryOptions RetryOptions { diff --git a/Microsoft.Azure.Cosmos/src/CosmosClient.cs b/Microsoft.Azure.Cosmos/src/CosmosClient.cs index d622e9ddcb..7b143196fe 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClient.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClient.cs @@ -28,7 +28,7 @@ namespace Microsoft.Azure.Cosmos /// /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// /// This example create a , , and a . @@ -97,7 +97,7 @@ namespace Microsoft.Azure.Cosmos /// /// /// - /// Performance Tips + /// Performance Tips /// Diagnose and troubleshoot issues /// Global data distribution /// Partitioning and horizontal scaling @@ -160,7 +160,7 @@ protected CosmosClient() /// /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The connection string to the cosmos account. ex: AccountEndpoint=https://XXXXX.documents.azure.com:443/;AccountKey=SuperSecretKey; /// (Optional) client options @@ -187,7 +187,7 @@ protected CosmosClient() /// /// /// - /// Performance Tips + /// Performance Tips /// Diagnose and troubleshoot issues public CosmosClient( string connectionString, @@ -204,7 +204,7 @@ public CosmosClient( /// /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The cosmos service endpoint to use /// The cosmos account key or resource token to use to create the client. @@ -232,7 +232,7 @@ public CosmosClient( /// /// /// - /// Performance Tips + /// Performance Tips /// Diagnose and troubleshoot issues public CosmosClient( string accountEndpoint, @@ -250,7 +250,7 @@ public CosmosClient( /// /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The cosmos service endpoint to use /// AzureKeyCredential with master-key or resource token.. @@ -281,7 +281,7 @@ public CosmosClient( /// /// /// - /// Performance Tips + /// Performance Tips /// Diagnose and troubleshoot issues /// /// AzureKeyCredential enables changing/updating master-key/ResourceToken whle CosmosClient is still in use. @@ -302,7 +302,7 @@ public CosmosClient( /// /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// /// The returned reference doesn't guarantee credentials or connectivity validations because creation doesn't make any network calls. @@ -357,7 +357,7 @@ internal CosmosClient( /// connections before the first call to the service is made. Use this to obtain lower latency while startup of your application. /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The cosmos service endpoint to use /// The cosmos account key or resource token to use to create the client. @@ -413,7 +413,7 @@ public static async Task CreateAndInitializeAsync(string accountEn /// connections before the first call to the service is made. Use this to obtain lower latency while startup of your application. /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The cosmos service endpoint to use /// AzureKeyCredential with master-key or resource token. @@ -471,7 +471,7 @@ public static async Task CreateAndInitializeAsync(string accountEn /// connections before the first call to the service is made. Use this to obtain lower latency while startup of your application. /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The connection string to the cosmos account. ex: AccountEndpoint=https://XXXXX.documents.azure.com:443/;AccountKey=SuperSecretKey; /// Containers to be initialized identified by it's database name and container name. @@ -518,7 +518,7 @@ public static async Task CreateAndInitializeAsync(string connectio /// connections before the first call to the service is made. Use this to obtain lower latency while startup of your application. /// CosmosClient is thread-safe. Its recommended to maintain a single instance of CosmosClient per lifetime /// of the application which enables efficient connection management and performance. Please refer to the - /// performance guide. + /// performance guide. /// /// The cosmos service endpoint to use. /// The token to provide AAD token for authorization. diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs index 330738c00b..2c07f060f8 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs @@ -250,7 +250,7 @@ public int GatewayModeMaxConnectionLimit /// Default value is /// /// - /// For more information, see Connection policy: Use direct connection mode. + /// For more information, see Connection policy: Use direct connection mode. /// /// /// @@ -614,7 +614,7 @@ public Func HttpClientFactory /// Quorum Read allowed with eventual consistency account or consistent prefix account. /// internal bool EnableUpgradeConsistencyToLocalQuorum { get; set; } = false; - + /// /// Gets or sets the connection protocol when connecting to the Azure Cosmos service. /// @@ -624,7 +624,7 @@ public Func HttpClientFactory /// /// This setting is not used when is set to . /// Gateway mode only supports HTTPS. - /// For more information, see Connection policy: Use the TCP protocol. + /// For more information, see Connection policy: Use the HTTPS protocol. /// internal Protocol ConnectionProtocol { diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs index cb3ffe6c04..ad568475df 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs @@ -44,9 +44,9 @@ public virtual int GetFailedRequestCount() } /// - /// Gets the string field instance in the Azure CosmosDB database service. + /// Gets the string field instance in the Azure Cosmos DB database service. /// - /// The string field instance in the Azure CosmosDB database service. + /// The string field instance in the Azure Cosmos DB database service. /// /// implements lazy materialization and is only materialized when is called. /// diff --git a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs index 8b72bfffa4..9c8c2fbb4e 100644 --- a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs +++ b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs @@ -349,7 +349,7 @@ public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout) /// Sets the connection mode to Direct. This is used by the client when connecting to the Azure Cosmos DB service. /// /// - /// For more information, see Connection policy: Use direct connection mode. + /// For more information, see Connection policy: Use direct connection mode. /// /// The current . /// @@ -396,7 +396,7 @@ public CosmosClientBuilder WithConnectionModeDirect() /// The default value is false. /// /// - /// For more information, see Connection policy: Use direct connection mode. + /// For more information, see Connection policy: Use direct connection mode. /// /// The current . /// @@ -472,7 +472,7 @@ internal CosmosClientBuilder WithDistributedTracingOptions(DistributedTracingOpt /// The number specifies the number of connections that may be opened simultaneously. Default is 50 connections /// Get or set the proxy information used for web requests. /// - /// For more information, see Connection policy: Use direct connection mode. + /// For more information, see Connection policy: Use direct connection mode. /// /// The current . /// @@ -529,7 +529,7 @@ public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandl /// If the cumulative wait time exceeds the this value, the client will stop retrying and return the error to the application. /// /// - /// For more information, see Handle rate limiting/request rate too large. + /// For more information, see Handle rate limiting/request rate too large. /// /// The current . /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/DedicatedGatewayRequestOptions.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/DedicatedGatewayRequestOptions.cs index 9ca854388c..31f82644a9 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/DedicatedGatewayRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/DedicatedGatewayRequestOptions.cs @@ -14,7 +14,7 @@ namespace Microsoft.Azure.Cosmos public class DedicatedGatewayRequestOptions { /// - /// Gets or sets the staleness value associated with the request in the Azure CosmosDB service. + /// Gets or sets the staleness value associated with the request in the Azure Cosmos DB service. /// /// Default value is null. /// @@ -24,7 +24,7 @@ public class DedicatedGatewayRequestOptions public TimeSpan? MaxIntegratedCacheStaleness { get; set; } /// - /// Gets or sets if bypass the integrated cache or not associated with the request in the Azure CosmosDB service. + /// Gets or sets if bypass the integrated cache or not associated with the request in the Azure Cosmos DB service. /// When set this value to true, the request will not be served from the integrated cache, and the response will not be cached either. /// /// Default value is false. diff --git a/Microsoft.Azure.Cosmos/src/RetryOptions.cs b/Microsoft.Azure.Cosmos/src/RetryOptions.cs index 8384df8105..5577b4b0d6 100644 --- a/Microsoft.Azure.Cosmos/src/RetryOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RetryOptions.cs @@ -56,7 +56,7 @@ public RetryOptions() /// retry after the time has elapsed. /// /// - /// For more information, see Handle rate limiting/request rate too large. + /// For more information, see Handle rate limiting/request rate too large. /// /// public int MaxRetryAttemptsOnThrottledRequests @@ -91,7 +91,7 @@ public int MaxRetryAttemptsOnThrottledRequests /// If the cumulative wait time exceeds the this value, the client will stop retrying and return the error to the application. /// /// - /// For more information, see Handle rate limiting/request rate too large. + /// For more information, see Handle rate limiting/request rate too large. /// /// public int MaxRetryWaitTimeInSeconds From 946dd4a95f27da4bc82ead3558d67afcd423c8c0 Mon Sep 17 00:00:00 2001 From: Mikhail Lipin Date: Wed, 16 Aug 2023 01:06:04 +0300 Subject: [PATCH 12/20] [Internal] Benchmark tool: Adds Cosmos Benchmark Metrics (#3950) * Adding metrics for Benchmark tool. * Adding OpenTelemetry. * Revert "Adding OpenTelemetry." This reverts commit c7da0884697064103145099e284892365f4ebb68. * Telemetry for windowed percentiles. * OpenTelemetry, AppInsights and Dashboard. * Removing DiagnosticDataListener. * Code styling, comments and clean-up. * Fixing issues with dashboard. * Fixing positions of charts on the dashboard. * Fixing the dashboard. * Updating titles and subtitles. * Removing ILogger and other not required references. * Fixing code review points. * Fixing issues after rebase. * Removing unnecessary changes. * Fixing code review points. * Adding metrics for Benchmark tool. * Adding OpenTelemetry. * Revert "Adding OpenTelemetry." This reverts commit c7da0884697064103145099e284892365f4ebb68. * Telemetry for windowed percentiles. * OpenTelemetry, AppInsights and Dashboard. * Removing DiagnosticDataListener. * Code styling, comments and clean-up. * Fixing issues with dashboard. * Fixing positions of charts on the dashboard. * Fixing the dashboard. * Updating titles and subtitles. * Removing ILogger and other not required references. * Fixing code review points. * Fixing issues after rebase. * Removing unnecessary changes. * Fixing code review points. * Fixing code review points. * make MetrcisCollectorProvider non static and remove locks * fix * fixes * use static class name TelemetrySpan.IncludePercentile * use app insights connection string * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs * rename AppInsightsConnectionString * fix * fix comments * fix if AppInsights c string is not set * summary * fix * remove unnecessary collector types * remove unnecesary metere provicer * add event source * remove folder * fix * split success and failed latencies * fix * fix --------- Co-authored-by: David Chaava Co-authored-by: David Chaava --- .../Tools/Benchmark/BenchmarkConfig.cs | 14 +- .../Tools/Benchmark/CosmosBenchmark.csproj | 1 + .../Tools/Benchmark/Fx/IExecutionStrategy.cs | 5 +- .../Tools/Benchmark/Fx/IExecutor.cs | 4 +- .../Tools/Benchmark/Fx/IMetricsCollector.cs | 36 ++++ .../Tools/Benchmark/Fx/MetricsCollector.cs | 166 ++++++++++++++++++ .../Benchmark/Fx/MetricsCollectorProvider.cs | 68 +++++++ .../Benchmark/Fx/ParallelExecutionStrategy.cs | 16 +- .../Benchmark/Fx/SerialOperationExecutor.cs | 31 +++- .../Tools/Benchmark/Fx/TelemetrySpan.cs | 53 +++++- .../Tools/Benchmark/MetricCollectionWindow.cs | 48 +++++ .../Tools/Benchmark/Program.cs | 67 +++++-- 12 files changed, 475 insertions(+), 34 deletions(-) create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IMetricsCollector.cs create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/MetricCollectionWindow.cs diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs index 6f89776370..f5ff091b28 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs @@ -6,12 +6,16 @@ namespace CosmosBenchmark { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Runtime; using CommandLine; using Microsoft.Azure.Documents.Client; using Newtonsoft.Json; + /// + /// Represents Benchmark Configuration + /// public class BenchmarkConfig { private static readonly string UserAgentSuffix = "cosmosdbdotnetbenchmark"; @@ -123,6 +127,12 @@ public class BenchmarkConfig [Option(Required = false, HelpText = "Container to publish results to")] public string ResultsContainer { get; set; } = "runsummary"; + [Option(Required = false, HelpText = "Metrics reporting interval in seconds")] + public int MetricsReportingIntervalInSec { get; set; } = 5; + + [Option(Required = false, HelpText = "Application Insights connection string")] + public string AppInsightsConnectionString { get; set; } + internal int GetTaskCount(int containerThroughput) { int taskCount = this.DegreeOfParallelism; @@ -263,11 +273,11 @@ private static void HandleParseError(IEnumerable errors) { foreach (Error e in errors) { - Console.WriteLine(e.ToString()); + Trace.TraceInformation(e.ToString()); } } Environment.Exit(errors.Count()); } } -} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj index c8ac26f5aa..2506e48102 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj @@ -17,6 +17,7 @@ + diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutionStrategy.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutionStrategy.cs index b18fc34dc7..ed1400026b 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutionStrategy.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutionStrategy.cs @@ -6,6 +6,7 @@ namespace CosmosBenchmark { using System; using System.Threading.Tasks; + using OpenTelemetry.Metrics; internal interface IExecutionStrategy { @@ -19,7 +20,7 @@ public Task ExecuteAsync( BenchmarkConfig benchmarkConfig, int serialExecutorConcurrency, int serialExecutorIterationCount, - double warmupFraction); - + double warmupFraction, + MetricsCollectorProvider metricsCollectorProvider); } } diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutor.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutor.cs index be2ee2e761..235a54199b 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutor.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IExecutor.cs @@ -17,6 +17,8 @@ public Task ExecuteAsync( int iterationCount, bool isWarmup, bool traceFailures, - Action completionCallback); + Action completionCallback, + BenchmarkConfig benchmarkConfig, + MetricsCollectorProvider metricsCollectorProvider); } } diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IMetricsCollector.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IMetricsCollector.cs new file mode 100644 index 0000000000..58af9d0b19 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/IMetricsCollector.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace CosmosBenchmark +{ + using System; + + /// + /// Represents the metrics collector. + /// + public interface IMetricsCollector + { + /// + /// Collects the number of successful operations. + /// + void CollectMetricsOnSuccess(); + + /// + /// Collects the number of failed operations. + /// + void CollectMetricsOnFailure(); + + /// + /// Records latency for success operations in milliseconda. + /// + /// The number of milliseconds to record. + void RecordSuccessOpLatencyAndRps(TimeSpan timeSpan); + + /// + /// Records latency for failed operations in milliseconda. + /// + /// The number of milliseconds to record. + void RecordFailedOpLatencyAndRps(TimeSpan timeSpan); + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs new file mode 100644 index 0000000000..866e400870 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs @@ -0,0 +1,166 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace CosmosBenchmark +{ + using System; + using System.Diagnostics.Metrics; + + /// + /// Represents the metrics collector. + /// + internal class MetricsCollector : IMetricsCollector + { + /// + /// Represents the meter to collect metrics. + /// + private readonly Meter meter; + + /// + /// Represents the histogram for operation latency. + /// + private readonly Histogram operationLatencyHistogram; + + /// + /// Represents the histogram for records per second metric. + /// + private readonly Histogram rpsMetricNameHistogram; + + /// + /// Represents the histogram for failed operation latency. + /// + private readonly Histogram operationFailedLatencyHistogram; + + /// + /// Represents the histogram failed operations for records per second metric. + /// + private readonly Histogram rpsFailedMetricNameHistogram; + + /// + /// Represents the success operation counter. + /// + private readonly Counter successOperationCounter; + + /// + /// Represents the failure operation counter. + /// + private readonly Counter failureOperationCounter; + + /// + /// Represents latency in milliseconds metric gauge. + /// + /// Please do not remove this as it used when collecting metrics.. + private readonly ObservableGauge latencyInMsMetricNameGauge; + + /// + /// Represents records per second metric gauge. + /// + /// Please do not remove this as it used when collecting metrics.. + private readonly ObservableGauge rpsNameGauge; + + + /// + /// Represents latency in milliseconds metric gauge for failed operations. + /// + /// Please do not remove this as it used when collecting metrics.. + private readonly ObservableGauge latencyInMsFailedMetricNameGauge; + + /// + /// Represents records per second metric gauge for failed operations. + /// + /// Please do not remove this as it used when collecting metrics.. + private readonly ObservableGauge rpsFailedNameGauge; + + /// + /// Latency in milliseconds. + /// + private double latencyInMs; + + /// + /// Records per second. + /// + private double rps; + + /// + /// Latency in milliseconds. + /// + private double latencyFailedInMs; + + /// + /// Records per second. + /// + private double rpsFailed; + + /// + /// Initialize new instance of . + /// + /// OpenTelemetry meter. + public MetricsCollector(Meter meter, string prefix) + { + this.meter = meter; + this.rpsMetricNameHistogram = meter.CreateHistogram($"{prefix}OperationRpsHistogram"); + this.operationLatencyHistogram = meter.CreateHistogram($"{prefix}OperationLatencyInMsHistogram"); + + this.rpsFailedMetricNameHistogram = meter.CreateHistogram($"{prefix}FailedOperationRpsHistogram"); + this.operationFailedLatencyHistogram = meter.CreateHistogram($"{prefix}FailedOperationLatencyInMsHistogram"); + + this.successOperationCounter = meter.CreateCounter($"{prefix}OperationSuccess"); + this.failureOperationCounter = meter.CreateCounter($"{prefix}OperationFailure"); + + this.latencyInMsMetricNameGauge = this.meter.CreateObservableGauge($"{prefix}OperationLatencyInMs", + () => new Measurement(this.latencyInMs)); + + this.rpsNameGauge = this.meter.CreateObservableGauge($"{prefix}OperationRps", + () => new Measurement(this.rps)); + + this.latencyInMsFailedMetricNameGauge = this.meter.CreateObservableGauge($"{prefix}FailedOperationLatencyInMs", + () => new Measurement(this.latencyInMs)); + + this.rpsFailedNameGauge = this.meter.CreateObservableGauge($"{prefix}FailedOperationRps", + () => new Measurement(this.rps)); + } + + /// + /// Collects the number of successful operations. + /// + public void CollectMetricsOnSuccess() + { + this.successOperationCounter.Add(1); + } + + /// + /// Collects the number of failed operations. + /// + public void CollectMetricsOnFailure() + { + this.failureOperationCounter.Add(1); + } + + /// + /// Records success operation latency in milliseconds. + /// + /// The number of milliseconds to record. + public void RecordSuccessOpLatencyAndRps( + TimeSpan timeSpan) + { + this.rps = 1000 / timeSpan.Milliseconds; + this.latencyInMs = timeSpan.Milliseconds; + this.rpsMetricNameHistogram.Record(this.rps); + this.operationLatencyHistogram.Record(this.latencyInMs); + } + + /// + /// Records failed operation latency in milliseconds. + /// + /// The number of milliseconds to record. + public void RecordFailedOpLatencyAndRps( + TimeSpan timeSpan) + { + this.rpsFailed = 1000 / timeSpan.Milliseconds; + this.latencyFailedInMs = timeSpan.Milliseconds; + this.rpsFailedMetricNameHistogram.Record(this.rpsFailed); + this.operationFailedLatencyHistogram.Record(this.latencyFailedInMs); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs new file mode 100644 index 0000000000..4de7009be1 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs @@ -0,0 +1,68 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace CosmosBenchmark +{ + using System; + using System.Diagnostics.Metrics; + using OpenTelemetry.Metrics; + + /// + /// Represents the metrics collector provider. + /// + internal class MetricsCollectorProvider + { + private readonly MetricCollectionWindow metricCollectionWindow; + + private readonly MetricsCollector insertOperationMetricsCollector; + + private readonly MetricsCollector queryOperationMetricsCollector; + + private readonly MetricsCollector readOperationMetricsCollector; + + private readonly Meter insertOperationMeter = new("CosmosBenchmarkInsertOperationMeter"); + + private readonly Meter queryOperationMeter = new("CosmosBenchmarkQueryOperationMeter"); + + private readonly Meter readOperationMeter = new("CosmosBenchmarkReadOperationMeter"); + + private readonly MeterProvider meterProvider; + + public MetricsCollectorProvider(BenchmarkConfig config, MeterProvider meterProvider) + { + this.meterProvider = meterProvider; + this.insertOperationMetricsCollector ??= new MetricsCollector(this.insertOperationMeter, "Insert"); + this.queryOperationMetricsCollector ??= new MetricsCollector(this.queryOperationMeter, "Query"); + this.readOperationMetricsCollector ??= new MetricsCollector(this.readOperationMeter, "Read"); + this.metricCollectionWindow ??= new MetricCollectionWindow(config); + } + + /// + /// Gets the metric collector. + /// + /// Benchmark operation. + /// Benchmark configuration. + /// Metrics collector. + /// Thrown if provided benchmark operation is not covered supported to collect metrics. + public IMetricsCollector GetMetricsCollector(IBenchmarkOperation benchmarkOperation, BenchmarkConfig config) + { + MetricCollectionWindow metricCollectionWindow = this.metricCollectionWindow; + + // Reset metricCollectionWindow and flush. + if (!metricCollectionWindow.IsValid) + { + this.meterProvider.ForceFlush(); + metricCollectionWindow.Reset(config); + } + + return benchmarkOperation.OperationType switch + { + BenchmarkOperationType.Insert => this.insertOperationMetricsCollector, + BenchmarkOperationType.Query => this.queryOperationMetricsCollector, + BenchmarkOperationType.Read => this.readOperationMetricsCollector, + _ => throw new NotSupportedException($"The type of {nameof(benchmarkOperation)} is not supported for collecting metrics."), + }; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs index e5dc7bb58d..2348d4a995 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs @@ -11,6 +11,7 @@ namespace CosmosBenchmark using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; + using OpenTelemetry.Metrics; internal class ParallelExecutionStrategy : IExecutionStrategy { @@ -28,7 +29,8 @@ public async Task ExecuteAsync( BenchmarkConfig benchmarkConfig, int serialExecutorConcurrency, int serialExecutorIterationCount, - double warmupFraction) + double warmupFraction, + MetricsCollectorProvider metricsCollectorProvider) { IExecutor warmupExecutor = new SerialOperationExecutor( executorId: "Warmup", @@ -37,7 +39,9 @@ await warmupExecutor.ExecuteAsync( (int)(serialExecutorIterationCount * warmupFraction), isWarmup: true, traceFailures: benchmarkConfig.TraceFailures, - completionCallback: () => { }); + completionCallback: () => { }, + benchmarkConfig, + metricsCollectorProvider); IExecutor[] executors = new IExecutor[serialExecutorConcurrency]; for (int i = 0; i < serialExecutorConcurrency; i++) @@ -54,11 +58,13 @@ await warmupExecutor.ExecuteAsync( iterationCount: serialExecutorIterationCount, isWarmup: false, traceFailures: benchmarkConfig.TraceFailures, - completionCallback: () => Interlocked.Decrement(ref this.pendingExecutorCount)); + completionCallback: () => Interlocked.Decrement(ref this.pendingExecutorCount), + benchmarkConfig, + metricsCollectorProvider); } return await this.LogOutputStats( - benchmarkConfig, + benchmarkConfig, executors); } @@ -160,4 +166,4 @@ private async Task LogOutputStats( } } } -} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs index 1086c31bd5..b33218f65b 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs @@ -8,10 +8,12 @@ namespace CosmosBenchmark using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; + using static CosmosBenchmark.TelemetrySpan; internal class SerialOperationExecutor : IExecutor { private readonly IBenchmarkOperation operation; + private readonly string executorId; public SerialOperationExecutor( @@ -26,6 +28,7 @@ public SerialOperationExecutor( } public int SuccessOperationCount { get; private set; } + public int FailedOperationCount { get; private set; } public double TotalRuCharges { get; private set; } @@ -34,27 +37,38 @@ public async Task ExecuteAsync( int iterationCount, bool isWarmup, bool traceFailures, - Action completionCallback) + Action completionCallback, + BenchmarkConfig benchmarkConfig, + MetricsCollectorProvider metricsCollectorProvider) { Trace.TraceInformation($"Executor {this.executorId} started"); + Trace.TraceInformation("Initializing counters and metrics."); + try { int currentIterationCount = 0; do { + IMetricsCollector metricsCollector = metricsCollectorProvider.GetMetricsCollector(this.operation, benchmarkConfig); + OperationResult? operationResult = null; await this.operation.PrepareAsync(); - using (IDisposable telemetrySpan = TelemetrySpan.StartNew( + using (ITelemetrySpan telemetrySpan = TelemetrySpan.StartNew( + benchmarkConfig, () => operationResult.Value, - disableTelemetry: isWarmup)) + disableTelemetry: isWarmup, + metricsCollector.RecordSuccessOpLatencyAndRps, + metricsCollector.RecordFailedOpLatencyAndRps)) { try { operationResult = await this.operation.ExecuteOnceAsync(); + metricsCollector.CollectMetricsOnSuccess(); + // Success case this.SuccessOperationCount++; this.TotalRuCharges += operationResult.Value.RuCharges; @@ -68,8 +82,11 @@ public async Task ExecuteAsync( { if (traceFailures) { - Console.WriteLine(ex.ToString()); + Trace.TraceInformation(ex.ToString()); } + telemetrySpan.MarkFailed(); + + metricsCollector.CollectMetricsOnFailure(); // failure case this.FailedOperationCount++; @@ -96,10 +113,14 @@ public async Task ExecuteAsync( Trace.TraceInformation($"Executor {this.executorId} completed"); } + catch (Exception e) + { + Trace.TraceInformation($"Error: {e.Message}"); + } finally { completionCallback(); } } } -} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs index 13d971aa2a..fff2acac3e 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs @@ -8,8 +8,9 @@ namespace CosmosBenchmark using System.Diagnostics; using System.Linq; using System.Threading; + using static CosmosBenchmark.TelemetrySpan; - internal struct TelemetrySpan : IDisposable + internal class TelemetrySpan : ITelemetrySpan { private static double[] latencyHistogram; private static int latencyIndex = -1; @@ -18,11 +19,18 @@ internal struct TelemetrySpan : IDisposable private Stopwatch stopwatch; private Func lazyOperationResult; + private Action recordFailedOpLatencyAction; + private Action recordSuccessOpLatencyAction; private bool disableTelemetry; + private bool isFailed = false; + private BenchmarkConfig benchmarkConfig; - public static IDisposable StartNew( + public static ITelemetrySpan StartNew( + BenchmarkConfig benchmarkConfig, Func lazyOperationResult, - bool disableTelemetry) + bool disableTelemetry, + Action recordSuccessOpLatencyAction, + Action recordFailedOpLatencyAction) { if (disableTelemetry || !TelemetrySpan.IncludePercentile) { @@ -31,12 +39,17 @@ public static IDisposable StartNew( return new TelemetrySpan { + benchmarkConfig = benchmarkConfig, stopwatch = Stopwatch.StartNew(), lazyOperationResult = lazyOperationResult, + recordSuccessOpLatencyAction = recordSuccessOpLatencyAction, + recordFailedOpLatencyAction = recordFailedOpLatencyAction, disableTelemetry = disableTelemetry }; } + public void MarkFailed() { this.isFailed = true; } + public void Dispose() { this.stopwatch.Stop(); @@ -47,6 +60,16 @@ public void Dispose() if (TelemetrySpan.IncludePercentile) { RecordLatency(this.stopwatch.Elapsed.TotalMilliseconds); + + if(this.isFailed) + { + this.recordSuccessOpLatencyAction?.Invoke(TimeSpan.FromMilliseconds(this.stopwatch.Elapsed.TotalMilliseconds)); + } + else + { + this.recordSuccessOpLatencyAction?.Invoke(TimeSpan.FromMilliseconds(this.stopwatch.Elapsed.TotalMilliseconds)); + + } } BenchmarkLatencyEventSource.Instance.LatencyDiagnostics( @@ -65,13 +88,13 @@ private static void RecordLatency(double elapsedMilliseoncds) internal static void ResetLatencyHistogram(int totalNumberOfIterations) { - latencyHistogram = new double[totalNumberOfIterations]; + TelemetrySpan.latencyHistogram = new double[totalNumberOfIterations]; latencyIndex = -1; } internal static double? GetLatencyPercentile(int percentile) { - if (latencyHistogram == null) + if (TelemetrySpan.latencyHistogram == null) { return null; } @@ -79,13 +102,31 @@ internal static void ResetLatencyHistogram(int totalNumberOfIterations) return MathNet.Numerics.Statistics.Statistics.Percentile(latencyHistogram.Take(latencyIndex + 1), percentile); } - private class NoOpDisposable : IDisposable + internal static double? GetLatencyQuantile(double quantile) + { + if (TelemetrySpan.latencyHistogram == null) + { + return null; + } + + return MathNet.Numerics.Statistics.Statistics.Quantile(latencyHistogram.Take(latencyIndex + 1), quantile); + } + + private class NoOpDisposable : ITelemetrySpan { public static readonly NoOpDisposable Instance = new NoOpDisposable(); public void Dispose() { } + + public void MarkFailed() + { + } + } + + public interface ITelemetrySpan : IDisposable { + void MarkFailed(); } } } diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/MetricCollectionWindow.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/MetricCollectionWindow.cs new file mode 100644 index 0000000000..948ade075e --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/MetricCollectionWindow.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace CosmosBenchmark +{ + using System; + + /// + /// Represents the metric collection window (time span while accumulating and granulating the data) + /// + internal class MetricCollectionWindow + { + /// + /// The timestamp when window span is started. + /// + public DateTime Started { get; private set; } + + /// + /// The timestamp until which the current window span is not elapsed. + /// + public DateTime ValidTill { get; private set; } + + /// + /// Creates the instance of . + /// + /// Cosmos Benchmark configuration. + public MetricCollectionWindow(BenchmarkConfig config) + { + this.Reset(config); + } + + /// + /// Indicates whether the current window is valid. + /// + public bool IsValid => DateTime.UtcNow > this.ValidTill; + + /// + /// Resets the started timestamp and valid till timespan. + /// + /// + public void Reset(BenchmarkConfig config) + { + this.Started = DateTime.UtcNow; + this.ValidTill = this.Started.AddSeconds(config.MetricsReportingIntervalInSec); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs index 99131262d7..2e7687932f 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs @@ -14,8 +14,13 @@ namespace CosmosBenchmark using System.Reflection; using System.Threading; using System.Threading.Tasks; + using Azure.Monitor.OpenTelemetry.Exporter; using Microsoft.Azure.Cosmos; + using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; + using OpenTelemetry; + using OpenTelemetry.Metrics; + using Container = Microsoft.Azure.Cosmos.Container; /// /// This sample demonstrates how to achieve high performance writes using Azure Comsos DB. @@ -31,8 +36,12 @@ public static async Task Main(string[] args) try { BenchmarkConfig config = BenchmarkConfig.From(args); - await Program.AddAzureInfoToRunSummary(); - + await AddAzureInfoToRunSummary(); + + MeterProvider meterProvider = BuildMeterProvider(config); + + MetricsCollectorProvider metricsCollectorProvider = new MetricsCollectorProvider(config, meterProvider); + ThreadPool.SetMinThreads(config.MinThreadPoolSize, config.MinThreadPoolSize); if (config.EnableLatencyPercentiles) @@ -45,7 +54,7 @@ public static async Task Main(string[] args) Program program = new Program(); - RunSummary runSummary = await program.ExecuteAsync(config); + RunSummary runSummary = await program.ExecuteAsync(config, metricsCollectorProvider); } finally { @@ -58,6 +67,37 @@ public static async Task Main(string[] args) } } + /// + /// Create a MeterProvider. If the App Insights connection string is not set, do not create an AppInsights Exporter. + /// + /// + private static MeterProvider BuildMeterProvider(BenchmarkConfig config) + { + if (string.IsNullOrWhiteSpace(config.AppInsightsConnectionString)) + { + return Sdk.CreateMeterProviderBuilder() + .AddMeter("CosmosBenchmarkInsertOperationMeter") + .AddMeter("CosmosBenchmarkQueryOperationMeter") + .AddMeter("CosmosBenchmarkReadOperationMeter") + .Build(); + } + + OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder = Sdk.CreateTracerProviderBuilder() + .AddAzureMonitorTraceExporter(); + + return Sdk.CreateMeterProviderBuilder() + .AddAzureMonitorMetricExporter(configure: new Action( + (options) => options.ConnectionString = config.AppInsightsConnectionString)) + .AddMeter("CosmosBenchmarkInsertOperationMeter") + .AddMeter("CosmosBenchmarkQueryOperationMeter") + .AddMeter("CosmosBenchmarkReadOperationMeter") + .Build(); + } + + /// + /// Adds Azure VM information to run summary. + /// + /// private static async Task AddAzureInfoToRunSummary() { using HttpClient httpClient = new HttpClient(); @@ -81,11 +121,13 @@ private static async Task AddAzureInfoToRunSummary() } } + /// /// Executing benchmarks for V2/V3 cosmosdb SDK. /// /// a Task object. - private async Task ExecuteAsync(BenchmarkConfig config) + private async Task ExecuteAsync(BenchmarkConfig config, + MetricsCollectorProvider metricsCollectorProvider) { // V3 SDK client initialization using (CosmosClient cosmosClient = config.CreateCosmosClient(config.Key)) @@ -137,7 +179,7 @@ private async Task ExecuteAsync(BenchmarkConfig config) } IExecutionStrategy execution = IExecutionStrategy.StartNew(benchmarkOperationFactory); - runSummary = await execution.ExecuteAsync(config, taskCount, opsPerTask, 0.01); + runSummary = await execution.ExecuteAsync(config, taskCount, opsPerTask, 0.01, metricsCollectorProvider); } if (config.CleanupOnFinish) @@ -154,13 +196,12 @@ private async Task ExecuteAsync(BenchmarkConfig config) } runSummary.ConsistencyLevel = consistencyLevel; - if (config.PublishResults) { runSummary.Diagnostics = CosmosDiagnosticsLogger.GetDiagnostics(); await this.PublishResults( - config, - runSummary, + config, + runSummary, cosmosClient); } @@ -169,8 +210,8 @@ await this.PublishResults( } private async Task PublishResults( - BenchmarkConfig config, - RunSummary runSummary, + BenchmarkConfig config, + RunSummary runSummary, CosmosClient benchmarkClient) { if (string.IsNullOrEmpty(config.ResultsEndpoint)) @@ -266,8 +307,8 @@ private static async Task CreatePartitionedContainerAsync(Ben { return await container.ReadContainerAsync(); } - catch(CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { + catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { // Show user cost of running this test double estimatedCostPerMonth = 0.06 * options.Throughput; double estimatedCostPerHour = estimatedCostPerMonth / (24 * 30); @@ -288,4 +329,4 @@ private static void ClearCoreSdkListeners() traceSource.Listeners.Clear(); } } -} +} \ No newline at end of file From 48af69ecab18066a6b4faef924cdc8fe7c828401 Mon Sep 17 00:00:00 2001 From: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com> Date: Wed, 16 Aug 2023 16:24:03 -0700 Subject: [PATCH 13/20] GatewayAddressCache: Fixes Unobserved Exception During Background Address Refresh (#4039) * Code changes to fix unobserved exception during background address refresh. * Code changes to add exception handler in task. * Code changes to fix null ref exception. * Revert "Code changes to fix null ref exception." This reverts commit 83f90d578bd301339f6fa13981a0fe2fc3d65fa6. * Revert "Code changes to add exception handler in task." This reverts commit c49ed8162758217a09df28417a6f76649eab6a26. * Code changes to address review comments. * Revert "Code changes to address review comments." This reverts commit d2b9f6b501f64f1a50b8a49de3ea76fbb9b5c853. --- .../src/Routing/GatewayAddressCache.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/Routing/GatewayAddressCache.cs b/Microsoft.Azure.Cosmos/src/Routing/GatewayAddressCache.cs index a4dbb1d8a3..8bc429070e 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/GatewayAddressCache.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/GatewayAddressCache.cs @@ -302,14 +302,27 @@ public async Task TryGetAddressesAsync( .ReplicaTransportAddressUris .Any(x => x.ShouldRefreshHealthStatus())) { - Task refreshAddressesInBackgroundTask = Task.Run(async () => await this.serverPartitionAddressCache.RefreshAsync( - key: partitionKeyRangeIdentity, - singleValueInitFunc: (currentCachedValue) => this.GetAddressesForRangeIdAsync( - request, - cachedAddresses: currentCachedValue, + Task refreshAddressesInBackgroundTask = Task.Run(async () => + { + try + { + await this.serverPartitionAddressCache.RefreshAsync( + key: partitionKeyRangeIdentity, + singleValueInitFunc: (currentCachedValue) => this.GetAddressesForRangeIdAsync( + request, + cachedAddresses: currentCachedValue, + partitionKeyRangeIdentity.CollectionRid, + partitionKeyRangeIdentity.PartitionKeyRangeId, + forceRefresh: true)); + } + catch (Exception ex) + { + DefaultTrace.TraceWarning("Failed to refresh addresses in the background for the collection rid: {0} with exception: {1}. '{2}'", partitionKeyRangeIdentity.CollectionRid, - partitionKeyRangeIdentity.PartitionKeyRangeId, - forceRefresh: true))); + ex, + System.Diagnostics.Trace.CorrelationManager.ActivityId); + } + }); } return addresses; From ce38cc1b2213cb04972f3c64dea9bb19e14d5334 Mon Sep 17 00:00:00 2001 From: Rinat Minibaev <132935507+rinatmini@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:12:46 -0700 Subject: [PATCH 14/20] Documentation: Adds additional note for GetContactedRegions method (#4042) * Added small remark for GetContactedRegions method documentation * Moved to remarks --- Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs index ad568475df..a82ef2ecc2 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs @@ -80,6 +80,9 @@ public virtual int GetFailedRequestCount() /// Gets the list of all regions that were contacted for a request /// /// The list of tuples containing the Region name and the URI + /// + /// The returned list contains unique regions and doesn't guarantee ordering of the regions contacted from the first to the last + /// public abstract IReadOnlyList<(string regionName, Uri uri)> GetContactedRegions(); } } From 2257ca2fbe1f3bb5b4ca87ce9c93955c1ab480a1 Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Tue, 22 Aug 2023 16:26:27 +0530 Subject: [PATCH 15/20] [Internal] Client Telemetry: Adds Client Telemetry pipeline sending data to service (#3900) * first draft * comment other pipelines * pint variables * commnet other pipelines * added env variable * minor changes * update env variable * print env variable * add space in end * fix test * fix tests * fix test * fix tests * remove response interceptor * logs * debuug mode * 3failing test to print llgs * minor refactoring * 2nd windows-2019 * fix ct tests * 2remove debugging * fix tests * revert * ncomment pipelines * fix test * minor changes * release and emulator pipeline * update pipelines * ignore abstract class test * fixing pipeline * refactor code * change it to class name to run tests * added emulator setup * 1 temp commit * env variable * renames env variable * fix tests * add condition * fix tests * reorder env variable * revert pipeline * did some clean up * change to revert * Revert "change to revert" This reverts commit 03db3c104505dc7b8f3cea267835c92ca530f8f4. * fix typos * throw if exception intercepter is null * remove modelling changes * removed virtaul * Update Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs Co-authored-by: Matias Quaranta * added condition for pipelines * Revert "added condition for pipelines" This reverts commit f9a208cd28e01badee97a2eb770a486cea67c1f0. * changed cond * fix codn * more enhancement * testing for release pipeline * refactore code and using test category * added comments on test * refactor pipeline code * fix variables * fix pipeline --------- Co-authored-by: Matias Quaranta --- .../ClientTelemetryReleaseTests.cs | 164 +++ .../ClientTelemetryTests.cs | 1078 +--------------- .../ClientTelemetryTestsBase.cs | 1087 +++++++++++++++++ .../Utils/HttpHandlerHelper.cs | 16 +- azure-pipelines-official.yml | 37 +- templates/build-test.yml | 6 +- 6 files changed, 1360 insertions(+), 1028 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs new file mode 100644 index 0000000000..d31c95bfe8 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs @@ -0,0 +1,164 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Net.Http; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Fluent; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + /// + /// In Release pipeline, no need to mock Client Telemetry Service Call and Test will talk to the real database account. + /// If you are making changes in this file please make sure you are adding similar test in also. + /// + [TestClass] + [TestCategory("ClientTelemetryRelease")] + public class ClientTelemetryReleaseTests : ClientTelemetryTestsBase + { + public override CosmosClientBuilder GetBuilder() + { + string connectionString = ConfigurationManager.GetEnvironmentVariable("COSMOSDB_ACCOUNT_CONNECTION_STRING", null); + return new CosmosClientBuilder(connectionString: connectionString); + } + + /// + /// Returing null means do not return any hard codd response for any HTTP call. + /// + /// + /// + public override Task HttpHandlerRequestCallbackChecks(HttpRequestMessage request) + { + return null; + } + + [ClassInitialize] + public static new void ClassInitialize(TestContext context) + { + ClientTelemetryTestsBase.ClassInitialize(context); + + // It will go away in next PR + Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, "true"); + Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "1"); + Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, "https://tools.cosmos.azure.com/api/clienttelemetry/trace"); + } + + [ClassCleanup] + public static new void FinalCleanup() + { + ClientTelemetryTestsBase.FinalCleanup(); + } + + [TestInitialize] + public override void TestInitialize() + { + base.TestInitialize(); + } + + [TestCleanup] + public override async Task Cleanup() + { + await base.Cleanup(); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct, true)] + [DataRow(ConnectionMode.Gateway, true)] + [DataRow(ConnectionMode.Direct, false)] + [DataRow(ConnectionMode.Gateway, false)] + public override async Task PointSuccessOperationsTest(ConnectionMode mode, bool isAzureInstance) + { + await base.PointSuccessOperationsTest(mode, isAzureInstance); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task PointReadFailureOperationsTest(ConnectionMode mode) + { + await base.PointReadFailureOperationsTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task StreamReadFailureOperationsTest(ConnectionMode mode) + { + await base.StreamReadFailureOperationsTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task StreamOperationsTest(ConnectionMode mode) + { + await base.StreamOperationsTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task BatchOperationsTest(ConnectionMode mode) + { + await base.BatchOperationsTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task SingleOperationMultipleTimesTest(ConnectionMode mode) + { + await base.SingleOperationMultipleTimesTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task QueryOperationSinglePartitionTest(ConnectionMode mode) + { + await base.QueryOperationSinglePartitionTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task QueryMultiPageSinglePartitionOperationTest(ConnectionMode mode) + { + await base.QueryMultiPageSinglePartitionOperationTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task QueryOperationCrossPartitionTest(ConnectionMode mode) + { + await base.QueryOperationCrossPartitionTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task QueryOperationMutiplePageCrossPartitionTest(ConnectionMode mode) + { + await base.QueryOperationMutiplePageCrossPartitionTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + [DataRow(ConnectionMode.Gateway)] + public override async Task QueryOperationInvalidContinuationTokenTest(ConnectionMode mode) + { + await base.QueryOperationInvalidContinuationTokenTest(mode); + } + + [TestMethod] + [DataRow(ConnectionMode.Direct)] + public override async Task CreateItemWithSubStatusCodeTest(ConnectionMode mode) + { + await base.CreateItemWithSubStatusCodeTest(mode); + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs index 30edc7ebf7..2ffc60f5f4 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs @@ -4,1121 +4,155 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests { - using System; - using System.Collections.Generic; - using System.Text; - using System.Threading.Tasks; - using System.Net; - using System.Net.Http; - using System.Reflection; using Microsoft.Azure.Cosmos.Fluent; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Cosmos.Telemetry; - using Microsoft.Azure.Cosmos.Handler; - using Microsoft.Azure.Documents; - using Newtonsoft.Json.Linq; - using Newtonsoft.Json; - using Documents.Rntbd; - using System.Globalization; - using System.Linq; - using Cosmos.Util; - using Microsoft.Azure.Cosmos.Telemetry.Models; + using System.Net.Http; + using System.Net; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + /// + /// In Emulator Mode, Run test against emulator and mock client telemetry service calls. + /// If you are making changes in this file please make sure you are adding similar test in also. + /// [TestClass] - public class ClientTelemetryTests : BaseCosmosClientHelper + [TestCategory("ClientTelemetryEmulator")] + public class ClientTelemetryTests : ClientTelemetryTestsBase { - private const int scheduledInSeconds = 1; - private static readonly object jsonObject = JsonConvert.DeserializeObject("{\"compute\":{\"azEnvironment\":\"AzurePublicCloud\",\"customData\":\"\",\"isHostCompatibilityLayerVm\":\"false\",\"licenseType\":\"\",\"location\":\"eastus\",\"name\":\"sourabh-testing\",\"offer\":\"UbuntuServer\",\"osProfile\":{\"adminUsername\":\"azureuser\",\"computerName\":\"sourabh-testing\"},\"osType\":\"Linux\",\"placementGroupId\":\"\",\"plan\":{\"name\":\"\",\"product\":\"\",\"publisher\":\"\"},\"platformFaultDomain\":\"0\",\"platformUpdateDomain\":\"0\",\"provider\":\"Microsoft.Compute\",\"publicKeys\":[{\"keyData\":\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5uCeOAm3ehmhI+2PbMoMl17Eo\r\nqfHKCycSaBJsv9qxlmBOuFheSJc1XknJleXUSsuTO016/d1PyWpevnqOZNRksWoa\r\nJvQ23sDTxcK+X2OP3QlCUeX4cMjPXqlL8z1UYzU4Bx3fFvf8fs67G3N72sxWBw5P\r\nZyuXyhBm0NCe/2NYMKgEDT4ma8XszO0ikbhoPKbMbgHAQk/ktWQHNcqYOPQKEWqp\r\nEK1R0rjS2nmtovfScP/ZGXcvOpJ1/NDBo4dh1K+OxOGM/4PSH/F448J5Zy4eAyEk\r\nscys+IpeIOTOlRUy/703SNIX0LEWlnYqbyL9c1ypcYLQqF76fKkDfzzFI/OWVlGw\r\nhj/S9uP8iMsR+fhGIbn6MAa7O4DWPWLuedSp7KDYyjY09gqNJsfuaAJN4LiC6bPy\r\nhknm0PVLK3ux7EUOt+cZrHCdIFWbdOtxiPNIl1tkv9kV5aE5Aj2gJm4MeB9uXYhS\r\nOuksboBc0wyUGrl9+XZJ1+NlZOf7IjVi86CieK8= generated-by-azure\r\n\",\"path\":\"/home/azureuser/.ssh/authorized_keys\"}],\"publisher\":\"Canonical\",\"resourceGroupName\":\"sourabh-telemetry-sdk\",\"resourceId\":\"/subscriptions/8fba6d4f-7c37-4d13-9063-fd58ad2b86e2/resourceGroups/sourabh-telemetry-sdk/providers/Microsoft.Compute/virtualMachines/sourabh-testing\",\"securityProfile\":{\"secureBootEnabled\":\"false\",\"virtualTpmEnabled\":\"false\"},\"sku\":\"18.04-LTS\",\"storageProfile\":{\"dataDisks\":[],\"imageReference\":{\"id\":\"\",\"offer\":\"UbuntuServer\",\"publisher\":\"Canonical\",\"sku\":\"18.04-LTS\",\"version\":\"latest\"},\"osDisk\":{\"caching\":\"ReadWrite\",\"createOption\":\"FromImage\",\"diffDiskSettings\":{\"option\":\"\"},\"diskSizeGB\":\"30\",\"encryptionSettings\":{\"enabled\":\"false\"},\"image\":{\"uri\":\"\"},\"managedDisk\":{\"id\":\"/subscriptions/8fba6d4f-7c37-4d13-9063-fd58ad2b86e2/resourceGroups/sourabh-telemetry-sdk/providers/Microsoft.Compute/disks/sourabh-testing_OsDisk_1_9a54abfc5ba149c6a106bd9e5b558c2a\",\"storageAccountType\":\"Premium_LRS\"},\"name\":\"sourabh-testing_OsDisk_1_9a54abfc5ba149c6a106bd9e5b558c2a\",\"osType\":\"Linux\",\"vhd\":{\"uri\":\"\"},\"writeAcceleratorEnabled\":\"false\"}},\"subscriptionId\":\"8fba6d4f-7c37-4d13-9063-fd58ad2b86e2\",\"tags\":\"azsecpack:nonprod;platformsettings.host_environment.service.platform_optedin_for_rootcerts:true\",\"tagsList\":[{\"name\":\"azsecpack\",\"value\":\"nonprod\"},{\"name\":\"platformsettings.host_environment.service.platform_optedin_for_rootcerts\",\"value\":\"true\"}],\"version\":\"18.04.202103250\",\"vmId\":\"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd\",\"vmScaleSetName\":\"\",\"vmSize\":\"Standard_D2s_v3\",\"zone\":\"1\"},\"network\":{\"interface\":[{\"ipv4\":{\"ipAddress\":[{\"privateIpAddress\":\"10.0.7.5\",\"publicIpAddress\":\"\"}],\"subnet\":[{\"address\":\"10.0.7.0\",\"prefix\":\"24\"}]},\"ipv6\":{\"ipAddress\":[]},\"macAddress\":\"000D3A8F8BA0\"}]}}"); - - private CosmosClientBuilder cosmosClientBuilder; - private static SystemUsageMonitor systemUsageMonitor; - - private List actualInfo; - private List preferredRegionList; + public override Task HttpHandlerRequestCallbackChecks(HttpRequestMessage request) + { + if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NoContent)); // In Emulator test, send hardcoded response status code as there is no real communication happens with client telemetry service + } - private IDictionary expectedMetricNameUnitMap; + return null; + } - private HttpClientHandlerHelper httpHandler; - private HttpClientHandlerHelper httpHandlerForNonAzureInstance; + public override CosmosClientBuilder GetBuilder() + { + return TestCommon.GetDefaultConfiguration(); + } [ClassInitialize] - public static void ClassInitialize(TestContext _) + public static new void ClassInitialize(TestContext context) { - SystemUsageMonitor oldSystemUsageMonitor = (SystemUsageMonitor)typeof(DiagnosticsHandlerHelper) - .GetField("systemUsageMonitor", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(DiagnosticsHandlerHelper.Instance); - oldSystemUsageMonitor.Stop(); - - ClientTelemetryTests.ResetSystemUsageMonitor(true); + ClientTelemetryTestsBase.ClassInitialize(context); } - [TestInitialize] - public void TestInitialize() + [ClassCleanup] + public static new void FinalCleanup() { - Util.EnableClientTelemetryEnvironmentVariables(); - - this.actualInfo = new List(); - - this.httpHandler = new HttpClientHandlerHelper - { - RequestCallBack = (request, cancellation) => - { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) - { - HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); - - string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - - lock (this.actualInfo) - { - this.actualInfo.Add(JsonConvert.DeserializeObject(jsonObject)); - } - - return Task.FromResult(result); - } - else if (request.RequestUri.AbsoluteUri.Equals(VmMetadataApiHandler.vmMetadataEndpointUrl.AbsoluteUri)) - { - HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); - - string payload = JsonConvert.SerializeObject(ClientTelemetryTests.jsonObject); - result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); - - return Task.FromResult(result); - } - return null; - } - }; - - this.httpHandlerForNonAzureInstance = new HttpClientHandlerHelper - { - RequestCallBack = (request, cancellation) => - { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) - { - HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); - - string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - - lock (this.actualInfo) - { - this.actualInfo.Add(JsonConvert.DeserializeObject(jsonObject)); - } - - return Task.FromResult(result); - } - return null; - } - }; - - this.preferredRegionList = new List - { - "region1", - "region2" - }; - - this.expectedMetricNameUnitMap = new Dictionary() - { - { ClientTelemetryOptions.CpuName, ClientTelemetryOptions.CpuUnit }, - { ClientTelemetryOptions.MemoryName, ClientTelemetryOptions.MemoryUnit }, - { ClientTelemetryOptions.AvailableThreadsName, ClientTelemetryOptions.AvailableThreadsUnit }, - { ClientTelemetryOptions.IsThreadStarvingName, ClientTelemetryOptions.IsThreadStarvingUnit }, - { ClientTelemetryOptions.ThreadWaitIntervalInMsName, ClientTelemetryOptions.ThreadWaitIntervalInMsUnit } - }; - - this.cosmosClientBuilder = TestCommon.GetDefaultConfiguration() - .WithApplicationPreferredRegions(this.preferredRegionList); + ClientTelemetryTestsBase.FinalCleanup(); } - private static void ResetSystemUsageMonitor(bool isTelemetryEnabled) + [TestInitialize] + public override void TestInitialize() { - ClientTelemetryTests.systemUsageMonitor?.Stop(); - - FieldInfo diagnosticsHandlerHelperInstance = typeof(DiagnosticsHandlerHelper) - .GetField("isTelemetryMonitoringEnabled", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); - diagnosticsHandlerHelperInstance.SetValue(null, isTelemetryEnabled); - - List recorders = new List() - { - (SystemUsageRecorder)typeof(DiagnosticsHandlerHelper) - .GetField("diagnosticSystemUsageRecorder", - BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(DiagnosticsHandlerHelper.Instance) - }; - - if (isTelemetryEnabled) - { - recorders.Add( - (SystemUsageRecorder)typeof(DiagnosticsHandlerHelper) - .GetField("telemetrySystemUsageRecorder", - BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(DiagnosticsHandlerHelper.Instance)); - } - - ClientTelemetryTests.systemUsageMonitor = SystemUsageMonitor.CreateAndStart(recorders); + base.TestInitialize(); } [TestCleanup] - public async Task Cleanup() + public override async Task Cleanup() { - FieldInfo isInitializedField = typeof(VmMetadataApiHandler).GetField("isInitialized", - BindingFlags.Static | - BindingFlags.NonPublic); - isInitializedField.SetValue(null, false); - - FieldInfo azMetadataField = typeof(VmMetadataApiHandler).GetField("azMetadata", - BindingFlags.Static | - BindingFlags.NonPublic); - azMetadataField.SetValue(null, null); - - await base.TestCleanup(); - - Util.DisableClientTelemetryEnvironmentVariables(); + await base.Cleanup(); } - [ClassCleanup] - public static void FinalCleanup() - { - ClientTelemetryTests.ResetSystemUsageMonitor(false); - } - [TestMethod] [DataRow(ConnectionMode.Direct, true)] [DataRow(ConnectionMode.Gateway, true)] [DataRow(ConnectionMode.Direct, false)] [DataRow(ConnectionMode.Gateway, false)] - public async Task PointSuccessOperationsTest(ConnectionMode mode, bool isAzureInstance) + public override async Task PointSuccessOperationsTest(ConnectionMode mode, bool isAzureInstance) { - Container container = await this.CreateClientAndContainer( - mode: mode, - isAzureInstance: isAzureInstance); - - // Create an item - ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue"); - ItemResponse createResponse = await container.CreateItemAsync(testItem); - ToDoActivity testItemCreated = createResponse.Resource; - - // Read an Item - await container.ReadItemAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); - - // Upsert an Item - await container.UpsertItemAsync(testItem); - - // Replace an Item - await container.ReplaceItemAsync(testItemCreated, testItemCreated.id.ToString()); - - // Patch an Item - List patch = new List() - { - PatchOperation.Add("/new", "patched") - }; - await ((ContainerInternal)container).PatchItemAsync( - testItem.id, - new Cosmos.PartitionKey(testItem.id), - patch); - - // Delete an Item - await container.DeleteItemAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Create.ToString(), 1}, - { Documents.OperationType.Upsert.ToString(), 1}, - { Documents.OperationType.Read.ToString(), 1}, - { Documents.OperationType.Replace.ToString(), 1}, - { Documents.OperationType.Patch.ToString(), 1}, - { Documents.OperationType.Delete.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 12, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - isAzureInstance: isAzureInstance); + await base.PointSuccessOperationsTest(mode, isAzureInstance); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task PointReadFailureOperationsTest(ConnectionMode mode) + public override async Task PointReadFailureOperationsTest(ConnectionMode mode) { - // Fail Read - try - { - Container container = await this.CreateClientAndContainer(mode, Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix); - - await container.ReadItemAsync( - new Guid().ToString(), - new Cosmos.PartitionKey(new Guid().ToString()), - new ItemRequestOptions() - { - BaseConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual // overriding client level consistency - }); - } - catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.NotFound) - { - string message = ce.ToString(); - Assert.IsNotNull(message); - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Read.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 2, - expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - expectedCacheSource: null, - isExpectedNetworkTelemetry: false); + await base.PointReadFailureOperationsTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task StreamReadFailureOperationsTest(ConnectionMode mode) + public override async Task StreamReadFailureOperationsTest(ConnectionMode mode) { - Container container = await this.CreateClientAndContainer(mode); - - // Fail Read - try - { - await container.ReadItemStreamAsync( - new Guid().ToString(), - new Cosmos.PartitionKey(new Guid().ToString()), - new ItemRequestOptions() - { - BaseConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix // Request level consistency - }); - } - catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.NotFound) - { - string message = ce.ToString(); - Assert.IsNotNull(message); - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Read.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 2, - expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - expectedCacheSource: null, - isExpectedNetworkTelemetry: false); + await base.StreamReadFailureOperationsTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task StreamOperationsTest(ConnectionMode mode) + public override async Task StreamOperationsTest(ConnectionMode mode) { - Container container = await this.CreateClientAndContainer(mode); - - // Create an item - var testItem = new { id = "MyTestItemId", partitionKeyPath = "MyTestPkValue", details = "it's working", status = "done" }; - await container - .CreateItemStreamAsync(TestCommon.SerializerCore.ToStream(testItem), - new Cosmos.PartitionKey(testItem.id)); - - //Upsert an Item - await container.UpsertItemStreamAsync(TestCommon.SerializerCore.ToStream(testItem), new Cosmos.PartitionKey(testItem.id)); - - //Read an Item - await container.ReadItemStreamAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); - - //Replace an Item - await container.ReplaceItemStreamAsync(TestCommon.SerializerCore.ToStream(testItem), testItem.id, new Cosmos.PartitionKey(testItem.id)); - - // Patch an Item - List patch = new List() - { - PatchOperation.Add("/new", "patched") - }; - await ((ContainerInternal)container).PatchItemStreamAsync( - partitionKey: new Cosmos.PartitionKey(testItem.id), - id: testItem.id, - patchOperations: patch); - - //Delete an Item - await container.DeleteItemStreamAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Create.ToString(), 1}, - { Documents.OperationType.Upsert.ToString(), 1}, - { Documents.OperationType.Read.ToString(), 1}, - { Documents.OperationType.Replace.ToString(), 1}, - { Documents.OperationType.Patch.ToString(), 1}, - { Documents.OperationType.Delete.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 12, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - expectedCacheSource: null); + await base.StreamOperationsTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task BatchOperationsTest(ConnectionMode mode) + public override async Task BatchOperationsTest(ConnectionMode mode) { - Container container = await this.CreateClientAndContainer(mode, Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual); // Client level consistency - using (BatchAsyncContainerExecutor executor = - new BatchAsyncContainerExecutor( - (ContainerInlineCore)container, - ((ContainerInlineCore)container).ClientContext, - 20, - Documents.Constants.MaxDirectModeBatchRequestBodySizeInBytes) - ) - { - List> tasks = new List>(); - for (int i = 0; i < 10; i++) - { - tasks.Add(executor.AddAsync(CreateItem(i.ToString()), NoOpTrace.Singleton, default)); - } - - await Task.WhenAll(tasks); - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Batch.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 2, - expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual, - expectedOperationRecordCountMap: expectedRecordCountInOperation); + await base.BatchOperationsTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task SingleOperationMultipleTimesTest(ConnectionMode mode) + public override async Task SingleOperationMultipleTimesTest(ConnectionMode mode) { - Container container = await this.CreateClientAndContainer(mode); - - // Create an item - ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); - - await container.CreateItemAsync(testItem, requestOptions: new ItemRequestOptions()); - - for (int count = 0; count < 50; count++) - { - // Read an Item - await container.ReadItemAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Read.ToString(), 50}, - { Documents.OperationType.Create.ToString(), 1} - }; - - await this.WaitAndAssert( - expectedOperationCount: 4,// 2 (read, requetLatency + requestCharge) + 2 (create, requestLatency + requestCharge) - expectedOperationRecordCountMap: expectedRecordCountInOperation); + await base.SingleOperationMultipleTimesTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task QueryOperationSinglePartitionTest(ConnectionMode mode) + public override async Task QueryOperationSinglePartitionTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); - - Container container = await this.CreateClientAndContainer(mode); - - ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue", "MyTestItemId"); - ItemRequestOptions requestOptions = new ItemRequestOptions() - { - ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix - }; - - ItemResponse createResponse = await container.CreateItemAsync( - item: testItem, - requestOptions: requestOptions); - - QueryRequestOptions queryRequestOptions = new QueryRequestOptions() - { - ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix, - }; - - List families = new List(); - if (createResponse.StatusCode == HttpStatusCode.Created) - { - string sqlQueryText = "SELECT * FROM c"; - - QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); - using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator( - queryDefinition: queryDefinition, - requestOptions: queryRequestOptions)) - { - while (queryResultSetIterator.HasMoreResults) - { - FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); - foreach (object family in currentResultSet) - { - families.Add(family); - } - } - } - - Assert.AreEqual(1, families.Count); - - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Query.ToString(), 1}, - { Documents.OperationType.Create.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 4, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix); + await base.QueryOperationSinglePartitionTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task QueryMultiPageSinglePartitionOperationTest(ConnectionMode mode) + public override async Task QueryMultiPageSinglePartitionOperationTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); - Container container = await this.CreateClientAndContainer(mode: mode); - - ItemRequestOptions requestOptions = new ItemRequestOptions() - { - ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix - }; - - ToDoActivity testItem1 = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue1", "MyTestItemId1"); - ItemResponse createResponse1 = await container.CreateItemAsync( - item: testItem1, - requestOptions: requestOptions); - ToDoActivity testItem2 = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue2", "MyTestItemId2"); - ItemResponse createResponse2 = await container.CreateItemAsync( - item: testItem2, - requestOptions: requestOptions); - - if (createResponse1.StatusCode == HttpStatusCode.Created && - createResponse2.StatusCode == HttpStatusCode.Created) - { - string sqlQueryText = "SELECT * FROM c"; - - List families = new List(); - QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); - using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator( - queryDefinition: queryDefinition, - requestOptions: new QueryRequestOptions() - { - ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix, - MaxItemCount = 1 - })) - { - while (queryResultSetIterator.HasMoreResults) - { - FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); - foreach (object family in currentResultSet) - { - families.Add(family); - } - } - } - - Assert.AreEqual(2, families.Count); - - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Query.ToString(), 3}, - { Documents.OperationType.Create.ToString(), 2} - }; - - await this.WaitAndAssert( - expectedOperationCount: 4, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix); + await base.QueryMultiPageSinglePartitionOperationTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task QueryOperationCrossPartitionTest(ConnectionMode mode) + public override async Task QueryOperationCrossPartitionTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); - - ContainerInternal itemsCore = (ContainerInternal)await this.CreateClientAndContainer( - mode: mode, - isLargeContainer: true); - - // Verify container has multiple partitions - int pkRangesCount = (await itemsCore.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(itemsCore.LinkUri)).Count; - Assert.IsTrue(pkRangesCount > 1, "Should have created a multi partition container."); - - Container container = (Container)itemsCore; - - await ToDoActivity.CreateRandomItems( - container: container, - pkCount: 2, - perPKItemCount: 5); - - string sqlQueryText = "SELECT * FROM c"; - - List families = new List(); - - QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); - using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator(queryDefinition)) - { - while (queryResultSetIterator.HasMoreResults) - { - FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); - foreach (object family in currentResultSet) - { - families.Add(family); - } - } - } - - Assert.AreEqual(10, families.Count); - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Query.ToString(), pkRangesCount}, - { Documents.OperationType.Create.ToString(), 10} - }; - - await this.WaitAndAssert( - expectedOperationCount: 4, - expectedOperationRecordCountMap: expectedRecordCountInOperation); + await base.QueryOperationCrossPartitionTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task QueryOperationMutiplePageCrossPartitionTest(ConnectionMode mode) + public override async Task QueryOperationMutiplePageCrossPartitionTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); - - ContainerInternal itemsCore = (ContainerInternal)await this.CreateClientAndContainer( - mode: mode, - isLargeContainer: true); - - // Verify container has multiple partitions - int pkRangesCount = (await itemsCore.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(itemsCore.LinkUri)).Count; - Assert.IsTrue(pkRangesCount > 1, "Should have created a multi partition container."); - - Container container = (Container)itemsCore; - - await ToDoActivity.CreateRandomItems( - container: container, - pkCount: 2, - perPKItemCount: 5); - - string sqlQueryText = "SELECT * FROM c"; - - List families = new List(); - QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); - using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator( - queryDefinition: queryDefinition, - requestOptions: new QueryRequestOptions() - { - MaxItemCount = 1 - })) - { - while (queryResultSetIterator.HasMoreResults) - { - FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); - foreach (object family in currentResultSet) - { - families.Add(family); - } - } - } - - Assert.AreEqual(10, families.Count); - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Query.ToString(), pkRangesCount + 10}, // 10 is number of items - { Documents.OperationType.Create.ToString(), 10} - }; - - await this.WaitAndAssert( - expectedOperationCount: 4, - expectedOperationRecordCountMap: expectedRecordCountInOperation); + await base.QueryOperationMutiplePageCrossPartitionTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] [DataRow(ConnectionMode.Gateway)] - public async Task QueryOperationInvalidContinuationTokenTest(ConnectionMode mode) + public override async Task QueryOperationInvalidContinuationTokenTest(ConnectionMode mode) { - Container container = await this.CreateClientAndContainer(mode); - - // Create an item : First successful request to load Cache - ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue"); - await container.CreateItemAsync(testItem); - - List results = new List(); - using (FeedIterator resultSetIterator = container.GetItemQueryIterator( - "SELECT * FROM c", - continuationToken: "dummy token")) - { - try - { - while (resultSetIterator.HasMoreResults) - { - FeedResponse response = await resultSetIterator.ReadNextAsync(); - results.AddRange(response); - } - } - catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.BadRequest) - { - string message = ce.ToString(); - Assert.IsNotNull(message); - } - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Create.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 2, - expectedOperationRecordCountMap: expectedRecordCountInOperation); + await base.QueryOperationInvalidContinuationTokenTest(mode); } [TestMethod] [DataRow(ConnectionMode.Direct)] - public async Task CreateItemWithSubStatusCodeTest(ConnectionMode mode) + public override async Task CreateItemWithSubStatusCodeTest(ConnectionMode mode) { - HttpClientHandlerHelper httpHandler = new HttpClientHandlerHelper(); - HttpClient httpClient = new HttpClient(httpHandler); - - httpHandler.RequestCallBack = (request, cancellation) => - { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) - { - HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); - - string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - - lock (this.actualInfo) - { - this.actualInfo.Add(JsonConvert.DeserializeObject(jsonObject)); - } - - return Task.FromResult(result); - } - else if (request.RequestUri.AbsoluteUri.Equals(VmMetadataApiHandler.vmMetadataEndpointUrl.AbsoluteUri)) - { - HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); - - string payload = JsonConvert.SerializeObject(ClientTelemetryTests.jsonObject); - result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); - - return Task.FromResult(result); - } - else if (request.Method == HttpMethod.Get && request.RequestUri.AbsolutePath == "//addresses/") - { - HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.Forbidden); - - // Add a substatus code that is not part of the enum. - // This ensures that if the backend adds a enum the status code is not lost. - result.Headers.Add(WFConstants.BackendHeaders.SubStatus, 999999.ToString(CultureInfo.InvariantCulture)); - - string payload = JsonConvert.SerializeObject(new Error() { Message = "test message" }); - result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); - - return Task.FromResult(result); - } - - return null; - }; - - // Replacing originally initialized cosmos Builder with this one with new handler - this.cosmosClientBuilder = this.cosmosClientBuilder - .WithHttpClientFactory(() => new HttpClient(httpHandler)); - - Container container = await this.CreateClientAndContainer( - mode: mode, - customHttpHandler: httpHandler); - try - { - ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue"); - ItemResponse createResponse = await container.CreateItemAsync(testItem); - Assert.Fail("Request should throw exception."); - } - catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.Forbidden) - { - Assert.AreEqual(999999, ce.SubStatusCode); - } - - IDictionary expectedRecordCountInOperation = new Dictionary - { - { Documents.OperationType.Create.ToString(), 1} - }; - - await this.WaitAndAssert(expectedOperationCount: 2, - expectedOperationRecordCountMap: expectedRecordCountInOperation, - expectedSubstatuscode: 999999, - isExpectedNetworkTelemetry: false); - + await base.CreateItemWithSubStatusCodeTest(mode); } - - /// - /// This method wait for the expected operations to get recorded by telemetry and assert the values - /// - /// Expected number of unique OperationInfo irrespective of response size. - /// Expected Consistency level of the operation recorded by telemetry - /// Expected number of requests recorded for each operation - /// - private async Task WaitAndAssert( - int expectedOperationCount = 0, - Microsoft.Azure.Cosmos.ConsistencyLevel? expectedConsistencyLevel = null, - IDictionary expectedOperationRecordCountMap = null, - int expectedSubstatuscode = 0, - bool? isAzureInstance = null, - string expectedCacheSource = "ClientCollectionCache", - bool isExpectedNetworkTelemetry = true) - { - Assert.IsNotNull(this.actualInfo, "Telemetry Information not available"); - - // As this feature is thread based execution so wait for the results to avoid test flakiness - List localCopyOfActualInfo = null; - ValueStopwatch stopwatch = ValueStopwatch.StartNew(); - - HashSet cacheRefreshInfoSet = new HashSet(); - do - { - await Task.Delay(TimeSpan.FromMilliseconds(1500)); // wait at least for 1 round of telemetry - - HashSet actualOperationSet = new HashSet(); - HashSet requestInfoSet = new HashSet(); - - lock (this.actualInfo) - { - // Setting the number of unique OperationInfo irrespective of response size as response size is varying in case of queries. - this.actualInfo - .ForEach(x => - { - if (x.CacheRefreshInfo != null && x.CacheRefreshInfo.Count > 0) - { - x.CacheRefreshInfo - .ForEach(y => - { - y.GreaterThan1Kb = false; - cacheRefreshInfoSet.Add(y); - }); - - } - - x.OperationInfo - .ForEach(y => - { - y.GreaterThan1Kb = false; - actualOperationSet.Add(y); - }); - }); - - if (actualOperationSet.Count == expectedOperationCount / 2) - { - // Copy the list to avoid it being modified while validating - localCopyOfActualInfo = new List(this.actualInfo); - break; - } - - Assert.IsTrue(stopwatch.Elapsed.TotalMinutes < 1, $"The expected operation count({expectedOperationCount}) was never hit, Actual Operation Count is {actualOperationSet.Count}. ActualInfo:{JsonConvert.SerializeObject(this.actualInfo)}"); - } - } - while (localCopyOfActualInfo == null); - - List actualOperationList = new List(); - List actualSystemInformation = new List(); - List actualRequestInformation = new List(); - - if (localCopyOfActualInfo[0].ConnectionMode == ConnectionMode.Direct.ToString().ToUpperInvariant()) - { - this.expectedMetricNameUnitMap.Add(ClientTelemetryOptions.NumberOfTcpConnectionName, ClientTelemetryOptions.NumberOfTcpConnectionUnit); - } - - ClientTelemetryTests.AssertAccountLevelInformation( - localCopyOfActualInfo: localCopyOfActualInfo, - actualOperationList: actualOperationList, - actualSystemInformation: actualSystemInformation, - actualRequestInformation: actualRequestInformation, - isAzureInstance: isAzureInstance); - - ClientTelemetryTests.AssertOperationLevelInformation( - expectedConsistencyLevel: expectedConsistencyLevel, - expectedOperationRecordCountMap: expectedOperationRecordCountMap, - actualOperationList: actualOperationList, - expectedSubstatuscode: expectedSubstatuscode); - - if(!string.IsNullOrEmpty(expectedCacheSource)) - { - Assert.IsTrue(cacheRefreshInfoSet.Count > 0, "Cache Refresh Information is not there"); - - ClientTelemetryTests.AssertCacheRefreshInfoInformation( - cacheRefreshInfoSet: cacheRefreshInfoSet, - expectedCacheSource: expectedCacheSource); - } - - ClientTelemetryTests.AssertSystemLevelInformation(actualSystemInformation, this.expectedMetricNameUnitMap); - if (localCopyOfActualInfo.First().ConnectionMode == ConnectionMode.Direct.ToString().ToUpperInvariant() - && isExpectedNetworkTelemetry) - { - ClientTelemetryTests.AssertNetworkLevelInformation(actualRequestInformation); - } - else - { - Assert.IsTrue(actualRequestInformation == null || actualRequestInformation.Count == 0, "Request Information is not expected in Gateway mode"); - } - } - - private static void AssertNetworkLevelInformation(List actualRequestInformation) - { - Assert.IsNotNull(actualRequestInformation); - Assert.IsTrue(actualRequestInformation.Count > 0); - - foreach(RequestInfo requestInfo in actualRequestInformation) - { - Assert.IsNotNull(requestInfo.Uri); - Assert.IsNotNull(requestInfo.DatabaseName); - Assert.IsNotNull(requestInfo.ContainerName); - Assert.IsNotNull(requestInfo.Operation); - Assert.IsNotNull(requestInfo.Resource); - Assert.IsNotNull(requestInfo.StatusCode); - Assert.AreNotEqual(0, requestInfo.StatusCode); - Assert.IsNotNull(requestInfo.SubStatusCode); - - Assert.IsNotNull(requestInfo.Metrics, "MetricInfo is null"); - } - } - - private static void AssertSystemLevelInformation(List actualSystemInformation, IDictionary expectedMetricNameUnitMap) - { - IDictionary actualMetricNameUnitMap = new Dictionary(); - - // Asserting If system information list is as expected - foreach (SystemInfo systemInfo in actualSystemInformation) - { - Assert.AreEqual("HostMachine", systemInfo.Resource); - Assert.IsNotNull(systemInfo.MetricInfo, "MetricInfo is null"); - - if(!actualMetricNameUnitMap.TryAdd(systemInfo.MetricInfo.MetricsName, systemInfo.MetricInfo.UnitName)) - { - Assert.AreEqual(systemInfo.MetricInfo.UnitName, actualMetricNameUnitMap[systemInfo.MetricInfo.MetricsName]); - } - - if(!systemInfo.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.IsThreadStarvingName) && - !systemInfo.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.ThreadWaitIntervalInMsName)) - { - Assert.IsTrue(systemInfo.MetricInfo.Count > 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Count is not greater than 0"); - Assert.IsNotNull(systemInfo.MetricInfo.Percentiles, $"Percentiles is null for metrics ({systemInfo.MetricInfo.MetricsName})"); - } - Assert.IsTrue(systemInfo.MetricInfo.Mean >= 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Mean is not greater than or equal to 0"); - Assert.IsTrue(systemInfo.MetricInfo.Max >= 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Max is not greater than or equal to 0"); - Assert.IsTrue(systemInfo.MetricInfo.Min >= 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Min is not greater than or equal to 0"); - if (systemInfo.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.CpuName)) - { - Assert.IsTrue(systemInfo.MetricInfo.Mean <= 100, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Mean is not greater than 100 for CPU Usage"); - Assert.IsTrue(systemInfo.MetricInfo.Max <= 100, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Max is not greater than 100 for CPU Usage"); - Assert.IsTrue(systemInfo.MetricInfo.Min <= 100, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Min is not greater than 100 for CPU Usage"); - }; - } - - Assert.IsTrue(expectedMetricNameUnitMap.EqualsTo(actualMetricNameUnitMap), $"Actual System Information metric i.e {string.Join(", ", actualMetricNameUnitMap)} is not matching with expected System Information Metric i.e. {string.Join(", ", expectedMetricNameUnitMap)}"); - - } - - private static void AssertOperationLevelInformation( - Microsoft.Azure.Cosmos.ConsistencyLevel? expectedConsistencyLevel, - IDictionary expectedOperationRecordCountMap, - List actualOperationList, - int expectedSubstatuscode = 0) - { - IDictionary actualOperationRecordCountMap = new Dictionary(); - // Asserting If operation list is as expected - foreach (OperationInfo operation in actualOperationList) - { - Assert.IsNotNull(operation.Operation, "Operation Type is null"); - Assert.IsNotNull(operation.Resource, "Resource Type is null"); - - Assert.AreEqual(expectedSubstatuscode, operation.SubStatusCode); - Assert.AreEqual(expectedConsistencyLevel?.ToString(), operation.Consistency, $"Consistency is not {expectedConsistencyLevel}"); - - Assert.IsNotNull(operation.MetricInfo, "MetricInfo is null"); - Assert.IsNotNull(operation.MetricInfo.MetricsName, "MetricsName is null"); - Assert.IsNotNull(operation.MetricInfo.UnitName, "UnitName is null"); - Assert.IsNotNull(operation.MetricInfo.Percentiles, "Percentiles is null"); - Assert.IsTrue(operation.MetricInfo.Count > 0, "MetricInfo Count is not greater than 0"); - Assert.IsTrue(operation.MetricInfo.Mean >= 0, "MetricInfo Mean is not greater than or equal to 0"); - Assert.IsTrue(operation.MetricInfo.Max >= 0, "MetricInfo Max is not greater than or equal to 0"); - Assert.IsTrue(operation.MetricInfo.Min >= 0, "MetricInfo Min is not greater than or equal to 0"); - if (operation.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.RequestLatencyName)) // putting this condition to avoid doubling of count as we have same information for each metrics - { - if (!actualOperationRecordCountMap.TryGetValue(operation.Operation.ToString(), out long recordCount)) - { - actualOperationRecordCountMap.Add(operation.Operation.ToString(), operation.MetricInfo.Count); - } - else - { - actualOperationRecordCountMap.Remove(operation.Operation.ToString()); - actualOperationRecordCountMap.Add(operation.Operation.ToString(), recordCount + operation.MetricInfo.Count); - } - } - } - - if (expectedOperationRecordCountMap != null) - { - Assert.IsTrue(expectedOperationRecordCountMap.EqualsTo(actualOperationRecordCountMap), $"actual record i.e. ({actualOperationRecordCountMap}) for operation does not match with expected record i.e. ({expectedOperationRecordCountMap})"); - } - } - - private static void AssertAccountLevelInformation( - List localCopyOfActualInfo, - List actualOperationList, - List actualSystemInformation, - List actualRequestInformation, - bool? isAzureInstance) - { - ISet machineId = new HashSet(); - - // Asserting If basic client telemetry object is as expected - foreach (ClientTelemetryProperties telemetryInfo in localCopyOfActualInfo) - { - if (telemetryInfo.OperationInfo != null) - { - actualOperationList.AddRange(telemetryInfo.OperationInfo); - } - - if (telemetryInfo.SystemInfo != null) - { - foreach (SystemInfo sysInfo in telemetryInfo.SystemInfo) - { - actualSystemInformation.Add(sysInfo); - } - } - - if (telemetryInfo.RequestInfo != null) - { - actualRequestInformation.AddRange(telemetryInfo.RequestInfo); - } - - if (telemetryInfo.ConnectionMode == ConnectionMode.Direct.ToString().ToUpperInvariant()) - { - Assert.AreEqual(6, telemetryInfo.SystemInfo.Count, $"System Information Count doesn't Match; {JsonConvert.SerializeObject(telemetryInfo.SystemInfo)}"); - } - else - { - Assert.AreEqual(5, telemetryInfo.SystemInfo.Count, $"System Information Count doesn't Match; {JsonConvert.SerializeObject(telemetryInfo.SystemInfo)}"); - } - - Assert.IsNotNull(telemetryInfo.GlobalDatabaseAccountName, "GlobalDatabaseAccountName is null"); - Assert.IsNotNull(telemetryInfo.DateTimeUtc, "Timestamp is null"); - Assert.AreEqual(2, telemetryInfo.PreferredRegions.Count); - Assert.AreEqual("region1", telemetryInfo.PreferredRegions[0]); - Assert.AreEqual("region2", telemetryInfo.PreferredRegions[1]); - Assert.AreEqual(1, telemetryInfo.AggregationIntervalInSec); - Assert.IsNull(telemetryInfo.AcceleratedNetworking); - Assert.IsNotNull(telemetryInfo.ClientId); - Assert.IsNotNull(telemetryInfo.ProcessId); - Assert.AreEqual(HashingExtension.ComputeHash(System.Diagnostics.Process.GetCurrentProcess().ProcessName), telemetryInfo.ProcessId); - Assert.IsNotNull(telemetryInfo.UserAgent); - Assert.IsFalse(telemetryInfo.UserAgent.Contains("userAgentSuffix"), "Useragent should not have suffix appended"); // Useragent should not contain useragentsuffix as it can have PII - Assert.IsNotNull(telemetryInfo.ConnectionMode); - - if(!string.IsNullOrEmpty(telemetryInfo.MachineId)) - { - machineId.Add(telemetryInfo.MachineId); - } - } - - if(isAzureInstance.HasValue) - { - if (isAzureInstance.Value) - { - Assert.AreEqual($"{VmMetadataApiHandler.VmIdPrefix}{"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd"}", machineId.First(), $"Generated Machine id is : {machineId.First()}"); - } - else - { - Assert.AreNotEqual($"{VmMetadataApiHandler.VmIdPrefix}{"d0cb93eb-214b-4c2b-bd3d-cc93e90d9efd"}", machineId.First(), $"Generated Machine id is : {machineId.First()}"); - Assert.AreEqual(1, machineId.Count, $"Multiple Machine Id has been generated i.e {JsonConvert.SerializeObject(machineId)}"); - } - } - } - - - private static void AssertCacheRefreshInfoInformation( - HashSet cacheRefreshInfoSet, - string expectedCacheSource) - { - foreach(CacheRefreshInfo cacheRefreshInfo in cacheRefreshInfoSet) - { - Assert.IsNotNull(cacheRefreshInfo.CacheRefreshSource); - Assert.IsTrue(expectedCacheSource.Contains(cacheRefreshInfo.CacheRefreshSource)); - Assert.IsNotNull(cacheRefreshInfo.Operation, "Operation Type is null"); - Assert.IsNotNull(cacheRefreshInfo.Resource, "Resource Type is null"); - Assert.IsNotNull(cacheRefreshInfo.StatusCode, "StatusCode is null"); - Assert.IsNotNull(cacheRefreshInfo.SubStatusCode); - Assert.IsNull(cacheRefreshInfo.Consistency); - Assert.IsNotNull(cacheRefreshInfo.ContainerName, "ContainerName is null"); - Assert.IsNotNull(cacheRefreshInfo.MetricInfo, "MetricInfo is null"); - Assert.IsNotNull(cacheRefreshInfo.MetricInfo.MetricsName, "MetricsName is null"); - Assert.IsNotNull(cacheRefreshInfo.MetricInfo.UnitName, "UnitName is null"); - Assert.IsNotNull(cacheRefreshInfo.MetricInfo.Percentiles, "Percentiles is null"); - Assert.IsTrue(cacheRefreshInfo.MetricInfo.Count >= 0, "MetricInfo Count is not greater than 0"); - Assert.IsTrue(cacheRefreshInfo.MetricInfo.Mean >= 0, "MetricInfo Mean is not greater than or equal to 0"); - Assert.IsTrue(cacheRefreshInfo.MetricInfo.Max >= 0, "MetricInfo Max is not greater than or equal to 0"); - Assert.IsTrue(cacheRefreshInfo.MetricInfo.Min >= 0, "MetricInfo Min is not greater than or equal to 0"); - } - } - - private static ItemBatchOperation CreateItem(string itemId) - { - var testItem = new { id = itemId, Status = itemId }; - return new ItemBatchOperation(Documents.OperationType.Create, 0, new Cosmos.PartitionKey(itemId), itemId, TestCommon.SerializerCore.ToStream(testItem)); - } - - private async Task CreateClientAndContainer(ConnectionMode mode, - Microsoft.Azure.Cosmos.ConsistencyLevel? consistency = null, - bool isLargeContainer = false, - bool isAzureInstance = false, - HttpClientHandlerHelper customHttpHandler = null) - { - if (consistency.HasValue) - { - this.cosmosClientBuilder = this.cosmosClientBuilder - .WithConsistencyLevel(consistency.Value); - } - - HttpClientHandlerHelper handlerHelper; - if (customHttpHandler == null) - { - handlerHelper = isAzureInstance ? this.httpHandler : this.httpHandlerForNonAzureInstance; - } - else - { - handlerHelper = customHttpHandler; - } - - this.cosmosClientBuilder = this.cosmosClientBuilder - .WithHttpClientFactory(() => new HttpClient(handlerHelper)) - .WithApplicationName("userAgentSuffix"); - - this.SetClient(mode == ConnectionMode.Gateway - ? this.cosmosClientBuilder.WithConnectionModeGateway().Build() - : this.cosmosClientBuilder.Build()); - - this.database = await this.GetClient().CreateDatabaseAsync(Guid.NewGuid().ToString()); - - return await this.database.CreateContainerAsync( - id: Guid.NewGuid().ToString(), - partitionKeyPath: "/id", - throughput: isLargeContainer? 15000 : 400); - - } - } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs new file mode 100644 index 0000000000..259a5346df --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs @@ -0,0 +1,1087 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Threading.Tasks; + using System.Net; + using System.Net.Http; + using System.Reflection; + using Microsoft.Azure.Cosmos.Fluent; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Handler; + using Microsoft.Azure.Documents; + using Newtonsoft.Json.Linq; + using Newtonsoft.Json; + using Documents.Rntbd; + using System.Globalization; + using System.Linq; + using Cosmos.Util; + using Microsoft.Azure.Cosmos.Telemetry.Models; + + public abstract class ClientTelemetryTestsBase : BaseCosmosClientHelper + { + private static SystemUsageMonitor systemUsageMonitor; + private static readonly List preferredRegionList = new List + { + Regions.EastUS, + Regions.WestUS2 + }; + + private static readonly IDictionary expectedMetricNameUnitMap = new Dictionary() + { + { ClientTelemetryOptions.CpuName, ClientTelemetryOptions.CpuUnit }, + { ClientTelemetryOptions.MemoryName, ClientTelemetryOptions.MemoryUnit }, + { ClientTelemetryOptions.AvailableThreadsName, ClientTelemetryOptions.AvailableThreadsUnit }, + { ClientTelemetryOptions.IsThreadStarvingName, ClientTelemetryOptions.IsThreadStarvingUnit }, + { ClientTelemetryOptions.ThreadWaitIntervalInMsName, ClientTelemetryOptions.ThreadWaitIntervalInMsUnit } + }; + + private List actualInfo; + protected CosmosClientBuilder cosmosClientBuilder; + + protected HttpClientHandlerHelper httpHandler; + protected HttpClientHandlerHelper httpHandlerForNonAzureInstance; + + private bool isClientTelemetryAPICallFailed = false; + + public static void ClassInitialize(TestContext _) + { + Util.EnableClientTelemetryEnvironmentVariables(); + + SystemUsageMonitor oldSystemUsageMonitor = (SystemUsageMonitor)typeof(DiagnosticsHandlerHelper) + .GetField("systemUsageMonitor", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(DiagnosticsHandlerHelper.Instance); + oldSystemUsageMonitor.Stop(); + + ClientTelemetryTestsBase.ResetSystemUsageMonitor(true); + } + + public virtual void TestInitialize() + { + this.actualInfo = new List(); + + this.httpHandler = new HttpClientHandlerHelper + { + RequestCallBack = (request, cancellation) => + { + if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + lock (this.actualInfo) + { + this.actualInfo.Add(JsonConvert.DeserializeObject(jsonObject)); + } + } + return this.HttpHandlerRequestCallbackChecks(request); + }, + ResponseIntercepter = (response) => + { + if (response.RequestMessage.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); + } + + return Task.FromResult(response); + }, + ExceptionIntercepter = (request, exception) => + { + if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + this.isClientTelemetryAPICallFailed = true; + } + } + }; + + this.httpHandlerForNonAzureInstance = new HttpClientHandlerHelper + { + RequestCallBack = (request, cancellation) => + { + if (request.RequestUri.AbsoluteUri.Equals(VmMetadataApiHandler.vmMetadataEndpointUrl.AbsoluteUri)) + { + HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.NotFound); + return Task.FromResult(result); + } + + if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + lock(this.actualInfo) + { + this.actualInfo.Add(JsonConvert.DeserializeObject(jsonObject)); + } + } + + return this.HttpHandlerRequestCallbackChecks(request); + }, + ResponseIntercepter = (response) => + { + if (response.RequestMessage.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); + } + return Task.FromResult(response); + }, + ExceptionIntercepter = (request, exception) => + { + if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + this.isClientTelemetryAPICallFailed = true; + } + } + }; + + this.cosmosClientBuilder = this.GetBuilder() + .WithApplicationPreferredRegions(ClientTelemetryTestsBase.preferredRegionList); + } + + public abstract Task HttpHandlerRequestCallbackChecks(HttpRequestMessage request); + + public abstract CosmosClientBuilder GetBuilder(); + + private static void ResetSystemUsageMonitor(bool isTelemetryEnabled) + { + ClientTelemetryTestsBase.systemUsageMonitor?.Stop(); + + FieldInfo diagnosticsHandlerHelperInstance = typeof(DiagnosticsHandlerHelper) + .GetField("isTelemetryMonitoringEnabled", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); + diagnosticsHandlerHelperInstance.SetValue(null, isTelemetryEnabled); + + List recorders = new List() + { + (SystemUsageRecorder)typeof(DiagnosticsHandlerHelper) + .GetField("diagnosticSystemUsageRecorder", + BindingFlags.Instance | BindingFlags.NonPublic) + .GetValue(DiagnosticsHandlerHelper.Instance) + }; + + if (isTelemetryEnabled) + { + recorders.Add( + (SystemUsageRecorder)typeof(DiagnosticsHandlerHelper) + .GetField("telemetrySystemUsageRecorder", + BindingFlags.Instance | BindingFlags.NonPublic) + .GetValue(DiagnosticsHandlerHelper.Instance)); + } + + ClientTelemetryTestsBase.systemUsageMonitor = SystemUsageMonitor.CreateAndStart(recorders); + } + + public virtual async Task Cleanup() + { + FieldInfo isInitializedField = typeof(VmMetadataApiHandler).GetField("isInitialized", + BindingFlags.Static | + BindingFlags.NonPublic); + isInitializedField.SetValue(null, false); + + FieldInfo azMetadataField = typeof(VmMetadataApiHandler).GetField("azMetadata", + BindingFlags.Static | + BindingFlags.NonPublic); + azMetadataField.SetValue(null, null); + await base.TestCleanup(); + + Assert.IsFalse(this.isClientTelemetryAPICallFailed, $"Call to client telemetry service endpoint (i.e. {ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri}) failed"); + } + + public static void FinalCleanup() + { + ClientTelemetryTestsBase.ResetSystemUsageMonitor(false); + } + + public virtual async Task PointSuccessOperationsTest(ConnectionMode mode, bool isAzureInstance) + { + Container container = await this.CreateClientAndContainer( + mode: mode, + isAzureInstance: isAzureInstance); + + // Create an item + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue"); + ItemResponse createResponse = await container.CreateItemAsync(testItem); + ToDoActivity testItemCreated = createResponse.Resource; + + // Read an Item + await container.ReadItemAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); + + // Upsert an Item + await container.UpsertItemAsync(testItem); + + // Replace an Item + await container.ReplaceItemAsync(testItemCreated, testItemCreated.id.ToString()); + + // Patch an Item + List patch = new List() + { + PatchOperation.Add("/new", "patched") + }; + await ((ContainerInternal)container).PatchItemAsync( + testItem.id, + new Cosmos.PartitionKey(testItem.id), + patch); + + // Delete an Item + await container.DeleteItemAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Create.ToString(), 1}, + { Documents.OperationType.Upsert.ToString(), 1}, + { Documents.OperationType.Read.ToString(), 1}, + { Documents.OperationType.Replace.ToString(), 1}, + { Documents.OperationType.Patch.ToString(), 1}, + { Documents.OperationType.Delete.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 12, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + isAzureInstance: isAzureInstance); + } + + public virtual async Task PointReadFailureOperationsTest(ConnectionMode mode) + { + // Fail Read + try + { + Container container = await this.CreateClientAndContainer(mode, Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix); + + await container.ReadItemAsync( + new Guid().ToString(), + new Cosmos.PartitionKey(new Guid().ToString()), + new ItemRequestOptions() + { + BaseConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual // overriding client level consistency + }); + } + catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.NotFound) + { + string message = ce.ToString(); + Assert.IsNotNull(message); + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Read.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 2, + expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + expectedCacheSource: null, + isExpectedNetworkTelemetry: false); + } + + public virtual async Task StreamReadFailureOperationsTest(ConnectionMode mode) + { + Container container = await this.CreateClientAndContainer(mode); + + // Fail Read + try + { + await container.ReadItemStreamAsync( + new Guid().ToString(), + new Cosmos.PartitionKey(new Guid().ToString()), + new ItemRequestOptions() + { + BaseConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix // Request level consistency + }); + } + catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.NotFound) + { + string message = ce.ToString(); + Assert.IsNotNull(message); + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Read.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 2, + expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + expectedCacheSource: null, + isExpectedNetworkTelemetry: false); + } + + public virtual async Task StreamOperationsTest(ConnectionMode mode) + { + Container container = await this.CreateClientAndContainer(mode); + + // Create an item + var testItem = new { id = "MyTestItemId", partitionKeyPath = "MyTestPkValue", details = "it's working", status = "done" }; + await container + .CreateItemStreamAsync(TestCommon.SerializerCore.ToStream(testItem), + new Cosmos.PartitionKey(testItem.id)); + + //Upsert an Item + await container.UpsertItemStreamAsync(TestCommon.SerializerCore.ToStream(testItem), new Cosmos.PartitionKey(testItem.id)); + + //Read an Item + await container.ReadItemStreamAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); + + //Replace an Item + await container.ReplaceItemStreamAsync(TestCommon.SerializerCore.ToStream(testItem), testItem.id, new Cosmos.PartitionKey(testItem.id)); + + // Patch an Item + List patch = new List() + { + PatchOperation.Add("/new", "patched") + }; + await ((ContainerInternal)container).PatchItemStreamAsync( + partitionKey: new Cosmos.PartitionKey(testItem.id), + id: testItem.id, + patchOperations: patch); + + //Delete an Item + await container.DeleteItemStreamAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Create.ToString(), 1}, + { Documents.OperationType.Upsert.ToString(), 1}, + { Documents.OperationType.Read.ToString(), 1}, + { Documents.OperationType.Replace.ToString(), 1}, + { Documents.OperationType.Patch.ToString(), 1}, + { Documents.OperationType.Delete.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 12, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + expectedCacheSource: null); + } + + public virtual async Task BatchOperationsTest(ConnectionMode mode) + { + Container container = await this.CreateClientAndContainer(mode, Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual); // Client level consistency + using (BatchAsyncContainerExecutor executor = + new BatchAsyncContainerExecutor( + (ContainerInlineCore)container, + ((ContainerInlineCore)container).ClientContext, + 20, + Documents.Constants.MaxDirectModeBatchRequestBodySizeInBytes) + ) + { + List> tasks = new List>(); + for (int i = 0; i < 10; i++) + { + tasks.Add(executor.AddAsync(CreateItem(i.ToString()), NoOpTrace.Singleton, default)); + } + + await Task.WhenAll(tasks); + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Batch.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 2, + expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.Eventual, + expectedOperationRecordCountMap: expectedRecordCountInOperation); + } + + public virtual async Task SingleOperationMultipleTimesTest(ConnectionMode mode) + { + Container container = await this.CreateClientAndContainer(mode); + + // Create an item + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity(); + + await container.CreateItemAsync(testItem, requestOptions: new ItemRequestOptions()); + + for (int count = 0; count < 50; count++) + { + // Read an Item + await container.ReadItemAsync(testItem.id, new Cosmos.PartitionKey(testItem.id)); + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Read.ToString(), 50}, + { Documents.OperationType.Create.ToString(), 1} + }; + + await this.WaitAndAssert( + expectedOperationCount: 4,// 2 (read, requestLatency + requestCharge) + 2 (create, requestLatency + requestCharge) + expectedOperationRecordCountMap: expectedRecordCountInOperation); + } + + public virtual async Task QueryOperationSinglePartitionTest(ConnectionMode mode) + { + Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); + + Container container = await this.CreateClientAndContainer(mode); + + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue", "MyTestItemId"); + ItemRequestOptions requestOptions = new ItemRequestOptions() + { + ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix + }; + + ItemResponse createResponse = await container.CreateItemAsync( + item: testItem, + requestOptions: requestOptions); + + QueryRequestOptions queryRequestOptions = new QueryRequestOptions() + { + EnableOptimisticDirectExecution = false, + ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix, + }; + + List families = new List(); + if (createResponse.StatusCode == HttpStatusCode.Created) + { + string sqlQueryText = "SELECT * FROM c"; + + QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); + using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator( + queryDefinition: queryDefinition, + requestOptions: queryRequestOptions)) + { + while (queryResultSetIterator.HasMoreResults) + { + FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); + foreach (object family in currentResultSet) + { + families.Add(family); + } + } + } + + Assert.AreEqual(1, families.Count); + + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Query.ToString(), 1}, + { Documents.OperationType.Create.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 4, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix); + } + + public virtual async Task QueryMultiPageSinglePartitionOperationTest(ConnectionMode mode) + { + Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); + Container container = await this.CreateClientAndContainer(mode: mode); + + ItemRequestOptions requestOptions = new ItemRequestOptions() + { + ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix + }; + + ToDoActivity testItem1 = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue1", "MyTestItemId1"); + ItemResponse createResponse1 = await container.CreateItemAsync( + item: testItem1, + requestOptions: requestOptions); + ToDoActivity testItem2 = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue2", "MyTestItemId2"); + ItemResponse createResponse2 = await container.CreateItemAsync( + item: testItem2, + requestOptions: requestOptions); + + if (createResponse1.StatusCode == HttpStatusCode.Created && + createResponse2.StatusCode == HttpStatusCode.Created) + { + string sqlQueryText = "SELECT * FROM c"; + + List families = new List(); + QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); + using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator( + queryDefinition: queryDefinition, + requestOptions: new QueryRequestOptions() + { + EnableOptimisticDirectExecution = false, + ConsistencyLevel = Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix, + MaxItemCount = 1 + })) + { + while (queryResultSetIterator.HasMoreResults) + { + FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); + foreach (object family in currentResultSet) + { + families.Add(family); + } + } + } + + Assert.AreEqual(2, families.Count); + + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Query.ToString(), 3}, + { Documents.OperationType.Create.ToString(), 2} + }; + + await this.WaitAndAssert( + expectedOperationCount: 4, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + expectedConsistencyLevel: Microsoft.Azure.Cosmos.ConsistencyLevel.ConsistentPrefix); + } + + public virtual async Task QueryOperationCrossPartitionTest(ConnectionMode mode) + { + Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); + + ContainerInternal itemsCore = (ContainerInternal)await this.CreateClientAndContainer( + mode: mode, + isLargeContainer: true); + + // Verify container has multiple partitions + int pkRangesCount = (await itemsCore.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(itemsCore.LinkUri)).Count; + Assert.IsTrue(pkRangesCount > 1, "Should have created a multi partition container."); + + Container container = (Container)itemsCore; + + await ToDoActivity.CreateRandomItems( + container: container, + pkCount: 2, + perPKItemCount: 5); + + string sqlQueryText = "SELECT * FROM c"; + + List families = new List(); + + QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); + using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator(queryDefinition)) + { + while (queryResultSetIterator.HasMoreResults) + { + FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); + foreach (object family in currentResultSet) + { + families.Add(family); + } + } + } + + Assert.AreEqual(10, families.Count); + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Query.ToString(), pkRangesCount}, + { Documents.OperationType.Create.ToString(), 10} + }; + + await this.WaitAndAssert( + expectedOperationCount: 4, + expectedOperationRecordCountMap: expectedRecordCountInOperation); + } + + public virtual async Task QueryOperationMutiplePageCrossPartitionTest(ConnectionMode mode) + { + ContainerInternal itemsCore = (ContainerInternal)await this.CreateClientAndContainer( + mode: mode, + isLargeContainer: true); + + // Verify container has multiple partitions + int pkRangesCount = (await itemsCore.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(itemsCore.LinkUri)).Count; + Assert.IsTrue(pkRangesCount > 1, "Should have created a multi partition container."); + + Container container = (Container)itemsCore; + + await ToDoActivity.CreateRandomItems( + container: container, + pkCount: 2, + perPKItemCount: 5); + + string sqlQueryText = "SELECT * FROM c"; + + List families = new List(); + QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); + using (FeedIterator queryResultSetIterator = container.GetItemQueryIterator( + queryDefinition: queryDefinition, + requestOptions: new QueryRequestOptions() + { + MaxItemCount = 1 + })) + { + while (queryResultSetIterator.HasMoreResults) + { + FeedResponse currentResultSet = await queryResultSetIterator.ReadNextAsync(); + foreach (object family in currentResultSet) + { + families.Add(family); + } + } + } + + Assert.AreEqual(10, families.Count); + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Query.ToString(), pkRangesCount + 10}, // 10 is number of items + { Documents.OperationType.Create.ToString(), 10} + }; + + await this.WaitAndAssert( + expectedOperationCount: 4, + expectedOperationRecordCountMap: expectedRecordCountInOperation); + } + + public virtual async Task QueryOperationInvalidContinuationTokenTest(ConnectionMode mode) + { + Container container = await this.CreateClientAndContainer(mode); + + // Create an item : First successful request to load Cache + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue"); + await container.CreateItemAsync(testItem); + + List results = new List(); + using (FeedIterator resultSetIterator = container.GetItemQueryIterator( + "SELECT * FROM c", + continuationToken: "dummy token")) + { + try + { + while (resultSetIterator.HasMoreResults) + { + FeedResponse response = await resultSetIterator.ReadNextAsync(); + results.AddRange(response); + } + } + catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.BadRequest) + { + string message = ce.ToString(); + Assert.IsNotNull(message); + } + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Create.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 2, + expectedOperationRecordCountMap: expectedRecordCountInOperation); + } + + public virtual async Task CreateItemWithSubStatusCodeTest(ConnectionMode mode) + { + HttpClientHandlerHelper httpHandler = new HttpClientHandlerHelper(); + HttpClient httpClient = new HttpClient(httpHandler); + + httpHandler.RequestCallBack = (request, cancellation) => + { + if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + { + string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + lock (this.actualInfo) + { + this.actualInfo.Add(JsonConvert.DeserializeObject(jsonObject)); + } + } + else if (request.Method == HttpMethod.Get && request.RequestUri.AbsolutePath == "//addresses/") + { + HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.Forbidden); + + // Add a substatus code that is not part of the enum. + // This ensures that if the backend adds a enum the status code is not lost. + result.Headers.Add(WFConstants.BackendHeaders.SubStatus, 999999.ToString(CultureInfo.InvariantCulture)); + + string payload = JsonConvert.SerializeObject(new Error() { Message = "test message" }); + result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); + + return Task.FromResult(result); + } + return null; + }; + + // Replacing originally initialized cosmos Builder with this one with new handler + this.cosmosClientBuilder = this.cosmosClientBuilder + .WithHttpClientFactory(() => new HttpClient(httpHandler)); + + Container container = await this.CreateClientAndContainer( + mode: mode, + customHttpHandler: httpHandler); + try + { + ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue"); + ItemResponse createResponse = await container.CreateItemAsync(testItem); + Assert.Fail("Request should throw exception."); + } + catch (CosmosException ce) when (ce.StatusCode == HttpStatusCode.Forbidden) + { + Assert.AreEqual(999999, ce.SubStatusCode); + } + + IDictionary expectedRecordCountInOperation = new Dictionary + { + { Documents.OperationType.Create.ToString(), 1} + }; + + await this.WaitAndAssert(expectedOperationCount: 2, + expectedOperationRecordCountMap: expectedRecordCountInOperation, + expectedSubstatuscode: 999999, + isExpectedNetworkTelemetry: false); + + } + + /// + /// This method wait for the expected operations to get recorded by telemetry and assert the values + /// + /// Expected number of unique OperationInfo irrespective of response size. + /// Expected Consistency level of the operation recorded by telemetry + /// Expected number of requests recorded for each operation + /// + private async Task WaitAndAssert( + int expectedOperationCount = 0, + Microsoft.Azure.Cosmos.ConsistencyLevel? expectedConsistencyLevel = null, + IDictionary expectedOperationRecordCountMap = null, + int expectedSubstatuscode = 0, + bool? isAzureInstance = null, + string expectedCacheSource = "ClientCollectionCache", + bool isExpectedNetworkTelemetry = true) + { + Assert.IsNotNull(this.actualInfo, "Telemetry Information not available"); + + // As this feature is thread based execution so wait for the results to avoid test flakiness + List localCopyOfActualInfo = null; + ValueStopwatch stopwatch = ValueStopwatch.StartNew(); + + HashSet cacheRefreshInfoSet = new HashSet(); + do + { + await Task.Delay(TimeSpan.FromMilliseconds(1500)); // wait at least for 1 round of telemetry + + HashSet actualOperationSet = new HashSet(); + HashSet requestInfoSet = new HashSet(); + + lock (this.actualInfo) + { + // Setting the number of unique OperationInfo irrespective of response size as response size is varying in case of queries. + this.actualInfo + .ForEach(x => + { + if (x.CacheRefreshInfo != null && x.CacheRefreshInfo.Count > 0) + { + x.CacheRefreshInfo + .ForEach(y => + { + y.GreaterThan1Kb = false; + cacheRefreshInfoSet.Add(y); + }); + + } + + x.OperationInfo + .ForEach(y => + { + y.GreaterThan1Kb = false; + actualOperationSet.Add(y); + }); + }); + + if (actualOperationSet.Count == expectedOperationCount / 2) + { + // Copy the list to avoid it being modified while validating + localCopyOfActualInfo = new List(this.actualInfo); + break; + } + + Assert.IsTrue(stopwatch.Elapsed.TotalMinutes < 1, $"The expected operation count({expectedOperationCount}) was never hit, Actual Operation Count is {actualOperationSet.Count}. ActualInfo:{JsonConvert.SerializeObject(this.actualInfo)}"); + } + } + while (localCopyOfActualInfo == null); + + List actualOperationList = new List(); + List actualSystemInformation = new List(); + List actualRequestInformation = new List(); + + if (localCopyOfActualInfo[0].ConnectionMode == ConnectionMode.Direct.ToString().ToUpperInvariant()) + { + ClientTelemetryTestsBase.expectedMetricNameUnitMap.TryAdd(ClientTelemetryOptions.NumberOfTcpConnectionName, ClientTelemetryOptions.NumberOfTcpConnectionUnit); + } + else + { + ClientTelemetryTestsBase.expectedMetricNameUnitMap.Remove(ClientTelemetryOptions.NumberOfTcpConnectionName); + } + + ClientTelemetryTestsBase.AssertAccountLevelInformation( + localCopyOfActualInfo: localCopyOfActualInfo, + actualOperationList: actualOperationList, + actualSystemInformation: actualSystemInformation, + actualRequestInformation: actualRequestInformation, + isAzureInstance: isAzureInstance); + + ClientTelemetryTestsBase.AssertOperationLevelInformation( + expectedConsistencyLevel: expectedConsistencyLevel, + expectedOperationRecordCountMap: expectedOperationRecordCountMap, + actualOperationList: actualOperationList, + expectedSubstatuscode: expectedSubstatuscode); + + if(!string.IsNullOrEmpty(expectedCacheSource)) + { + Assert.IsTrue(cacheRefreshInfoSet.Count > 0, "Cache Refresh Information is not there"); + + ClientTelemetryTestsBase.AssertCacheRefreshInfoInformation( + cacheRefreshInfoSet: cacheRefreshInfoSet, + expectedCacheSource: expectedCacheSource); + } + + ClientTelemetryTestsBase.AssertSystemLevelInformation(actualSystemInformation, ClientTelemetryTestsBase.expectedMetricNameUnitMap); + if (localCopyOfActualInfo.First().ConnectionMode == ConnectionMode.Direct.ToString().ToUpperInvariant()) + { + if (isExpectedNetworkTelemetry) + { + ClientTelemetryTestsBase.AssertNetworkLevelInformation(actualRequestInformation); + } + } + else + { + Assert.IsTrue(actualRequestInformation == null || actualRequestInformation.Count == 0, $"Request Information is not expected in {localCopyOfActualInfo.First().ConnectionMode} mode"); + } + } + + private static void AssertNetworkLevelInformation(List actualRequestInformation) + { + Assert.IsNotNull(actualRequestInformation); + Assert.IsTrue(actualRequestInformation.Count > 0); + + foreach(RequestInfo requestInfo in actualRequestInformation) + { + Assert.IsNotNull(requestInfo.Uri); + Assert.IsNotNull(requestInfo.DatabaseName); + Assert.IsNotNull(requestInfo.ContainerName); + Assert.IsNotNull(requestInfo.Operation); + Assert.IsNotNull(requestInfo.Resource); + Assert.IsNotNull(requestInfo.StatusCode); + Assert.AreNotEqual(0, requestInfo.StatusCode); + Assert.IsNotNull(requestInfo.SubStatusCode); + + Assert.IsNotNull(requestInfo.Metrics, "MetricInfo is null"); + } + } + + private static void AssertSystemLevelInformation(List actualSystemInformation, IDictionary expectedMetricNameUnitMap) + { + IDictionary actualMetricNameUnitMap = new Dictionary(); + + // Asserting If system information list is as expected + foreach (SystemInfo systemInfo in actualSystemInformation) + { + Assert.AreEqual("HostMachine", systemInfo.Resource); + Assert.IsNotNull(systemInfo.MetricInfo, "MetricInfo is null"); + + if(!actualMetricNameUnitMap.TryAdd(systemInfo.MetricInfo.MetricsName, systemInfo.MetricInfo.UnitName)) + { + Assert.AreEqual(systemInfo.MetricInfo.UnitName, actualMetricNameUnitMap[systemInfo.MetricInfo.MetricsName]); + } + + if(!systemInfo.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.IsThreadStarvingName) && + !systemInfo.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.ThreadWaitIntervalInMsName)) + { + Assert.IsTrue(systemInfo.MetricInfo.Count > 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Count is not greater than 0"); + Assert.IsNotNull(systemInfo.MetricInfo.Percentiles, $"Percentiles is null for metrics ({systemInfo.MetricInfo.MetricsName})"); + } + Assert.IsTrue(systemInfo.MetricInfo.Mean >= 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Mean is not greater than or equal to 0"); + Assert.IsTrue(systemInfo.MetricInfo.Max >= 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Max is not greater than or equal to 0"); + Assert.IsTrue(systemInfo.MetricInfo.Min >= 0, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Min is not greater than or equal to 0"); + if (systemInfo.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.CpuName)) + { + Assert.IsTrue(systemInfo.MetricInfo.Mean <= 100, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Mean is not greater than 100 for CPU Usage"); + Assert.IsTrue(systemInfo.MetricInfo.Max <= 100, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Max is not greater than 100 for CPU Usage"); + Assert.IsTrue(systemInfo.MetricInfo.Min <= 100, $"MetricInfo ({systemInfo.MetricInfo.MetricsName}) Min is not greater than 100 for CPU Usage"); + }; + } + + Assert.IsTrue(expectedMetricNameUnitMap.EqualsTo(actualMetricNameUnitMap), $"Actual System Information metric i.e {string.Join(", ", actualMetricNameUnitMap)} is not matching with expected System Information Metric i.e. {string.Join(", ", expectedMetricNameUnitMap)}"); + + } + + private static void AssertOperationLevelInformation( + Microsoft.Azure.Cosmos.ConsistencyLevel? expectedConsistencyLevel, + IDictionary expectedOperationRecordCountMap, + List actualOperationList, + int expectedSubstatuscode = 0) + { + IDictionary actualOperationRecordCountMap = new Dictionary(); + // Asserting If operation list is as expected + foreach (OperationInfo operation in actualOperationList) + { + Assert.IsNotNull(operation.Operation, "Operation Type is null"); + Assert.IsNotNull(operation.Resource, "Resource Type is null"); + + Assert.AreEqual(expectedSubstatuscode, operation.SubStatusCode); + Assert.AreEqual(expectedConsistencyLevel?.ToString(), operation.Consistency, $"Consistency is not {expectedConsistencyLevel}"); + + Assert.IsNotNull(operation.MetricInfo, "MetricInfo is null"); + Assert.IsNotNull(operation.MetricInfo.MetricsName, "MetricsName is null"); + Assert.IsNotNull(operation.MetricInfo.UnitName, "UnitName is null"); + Assert.IsNotNull(operation.MetricInfo.Percentiles, "Percentiles is null"); + Assert.IsTrue(operation.MetricInfo.Count > 0, "MetricInfo Count is not greater than 0"); + Assert.IsTrue(operation.MetricInfo.Mean >= 0, "MetricInfo Mean is not greater than or equal to 0"); + Assert.IsTrue(operation.MetricInfo.Max >= 0, "MetricInfo Max is not greater than or equal to 0"); + Assert.IsTrue(operation.MetricInfo.Min >= 0, "MetricInfo Min is not greater than or equal to 0"); + + if (operation.MetricInfo.MetricsName.Equals(ClientTelemetryOptions.RequestLatencyName)) // putting this condition to avoid doubling of count as we have same information for each metrics + { + if (!actualOperationRecordCountMap.TryGetValue(operation.Operation, out long recordCount)) + { + actualOperationRecordCountMap.Add(operation.Operation, operation.MetricInfo.Count); + } + else + { + actualOperationRecordCountMap.Remove(operation.Operation); + actualOperationRecordCountMap.Add(operation.Operation, recordCount + operation.MetricInfo.Count); + } + } + } + + if (expectedOperationRecordCountMap != null) + { + Assert.IsTrue(expectedOperationRecordCountMap.EqualsTo(actualOperationRecordCountMap), $"actual record i.e. ({string.Join(", ", actualOperationRecordCountMap)}) for operation does not match with expected record i.e. ({string.Join(", ", expectedOperationRecordCountMap)})"); + } + } + + private static void AssertAccountLevelInformation( + List localCopyOfActualInfo, + List actualOperationList, + List actualSystemInformation, + List actualRequestInformation, + bool? isAzureInstance) + { + ISet machineId = new HashSet(); + + // Asserting If basic client telemetry object is as expected + foreach (ClientTelemetryProperties telemetryInfo in localCopyOfActualInfo) + { + if (telemetryInfo.OperationInfo != null) + { + actualOperationList.AddRange(telemetryInfo.OperationInfo); + } + + if (telemetryInfo.SystemInfo != null) + { + foreach (SystemInfo sysInfo in telemetryInfo.SystemInfo) + { + actualSystemInformation.Add(sysInfo); + } + } + + if (telemetryInfo.RequestInfo != null) + { + actualRequestInformation.AddRange(telemetryInfo.RequestInfo); + } + + if (telemetryInfo.ConnectionMode == ConnectionMode.Direct.ToString().ToUpperInvariant()) + { + Assert.AreEqual(6, telemetryInfo.SystemInfo.Count, $"System Information Count doesn't Match; {JsonConvert.SerializeObject(telemetryInfo.SystemInfo)}"); + } + else + { + Assert.AreEqual(5, telemetryInfo.SystemInfo.Count, $"System Information Count doesn't Match; {JsonConvert.SerializeObject(telemetryInfo.SystemInfo)}"); + } + + Assert.IsNotNull(telemetryInfo.GlobalDatabaseAccountName, "GlobalDatabaseAccountName is null"); + Assert.IsNotNull(telemetryInfo.DateTimeUtc, "Timestamp is null"); + Assert.AreEqual(2, telemetryInfo.PreferredRegions.Count); + Assert.AreEqual(Regions.EastUS, telemetryInfo.PreferredRegions[0]); + Assert.AreEqual(Regions.WestUS2, telemetryInfo.PreferredRegions[1]); + Assert.AreEqual(1, telemetryInfo.AggregationIntervalInSec); + Assert.IsNull(telemetryInfo.AcceleratedNetworking); + Assert.IsNotNull(telemetryInfo.ClientId); + Assert.IsNotNull(telemetryInfo.ProcessId); + Assert.AreEqual(HashingExtension.ComputeHash(System.Diagnostics.Process.GetCurrentProcess().ProcessName), telemetryInfo.ProcessId); + Assert.IsNotNull(telemetryInfo.UserAgent); + Assert.IsFalse(telemetryInfo.UserAgent.Contains("userAgentSuffix"), "Useragent should not have suffix appended"); // Useragent should not contain useragentsuffix as it can have PII + Assert.IsNotNull(telemetryInfo.ConnectionMode); + + if (!string.IsNullOrEmpty(telemetryInfo.MachineId)) + { + machineId.Add(telemetryInfo.MachineId); + } + } + + if (isAzureInstance.HasValue) + { + if (isAzureInstance.Value) + { + Assert.IsTrue(machineId.First().StartsWith(VmMetadataApiHandler.VmIdPrefix), $"Generated Machine id is : {machineId.First()}"); + } + else + { + Assert.IsTrue(machineId.First().StartsWith(VmMetadataApiHandler.HashedMachineNamePrefix), $"Generated Machine id is : {machineId.First()}"); + } + } + + Assert.AreEqual(1, machineId.Count, $"Multiple Machine Id has been generated i.e {JsonConvert.SerializeObject(machineId)}"); + } + + private static void AssertCacheRefreshInfoInformation( + HashSet cacheRefreshInfoSet, + string expectedCacheSource) + { + foreach(CacheRefreshInfo cacheRefreshInfo in cacheRefreshInfoSet) + { + Assert.IsNotNull(cacheRefreshInfo.CacheRefreshSource); + Assert.IsTrue(expectedCacheSource.Contains(cacheRefreshInfo.CacheRefreshSource)); + Assert.IsNotNull(cacheRefreshInfo.Operation, "Operation Type is null"); + Assert.IsNotNull(cacheRefreshInfo.Resource, "Resource Type is null"); + Assert.IsNotNull(cacheRefreshInfo.StatusCode, "StatusCode is null"); + Assert.IsNotNull(cacheRefreshInfo.SubStatusCode); + Assert.IsNull(cacheRefreshInfo.Consistency); + Assert.IsNotNull(cacheRefreshInfo.ContainerName, "ContainerName is null"); + Assert.IsNotNull(cacheRefreshInfo.MetricInfo, "MetricInfo is null"); + Assert.IsNotNull(cacheRefreshInfo.MetricInfo.MetricsName, "MetricsName is null"); + Assert.IsNotNull(cacheRefreshInfo.MetricInfo.UnitName, "UnitName is null"); + Assert.IsNotNull(cacheRefreshInfo.MetricInfo.Percentiles, "Percentiles is null"); + Assert.IsTrue(cacheRefreshInfo.MetricInfo.Count >= 0, "MetricInfo Count is not greater than 0"); + Assert.IsTrue(cacheRefreshInfo.MetricInfo.Mean >= 0, "MetricInfo Mean is not greater than or equal to 0"); + Assert.IsTrue(cacheRefreshInfo.MetricInfo.Max >= 0, "MetricInfo Max is not greater than or equal to 0"); + Assert.IsTrue(cacheRefreshInfo.MetricInfo.Min >= 0, "MetricInfo Min is not greater than or equal to 0"); + } + } + + private static ItemBatchOperation CreateItem(string itemId) + { + var testItem = new { id = itemId, Status = itemId }; + return new ItemBatchOperation(Documents.OperationType.Create, 0, new Cosmos.PartitionKey(itemId), itemId, TestCommon.SerializerCore.ToStream(testItem)); + } + + private async Task CreateClientAndContainer(ConnectionMode mode, + Microsoft.Azure.Cosmos.ConsistencyLevel? consistency = null, + bool isLargeContainer = false, + bool isAzureInstance = true, + HttpClientHandlerHelper customHttpHandler = null) + { + if (consistency.HasValue) + { + this.cosmosClientBuilder = this.cosmosClientBuilder + .WithConsistencyLevel(consistency.Value); + } + + HttpClientHandlerHelper handlerHelper = customHttpHandler ?? (isAzureInstance ? this.httpHandler : this.httpHandlerForNonAzureInstance); + this.cosmosClientBuilder = this.cosmosClientBuilder + .WithHttpClientFactory(() => new HttpClient(handlerHelper)) + .WithApplicationName("userAgentSuffix"); + + this.SetClient(mode == ConnectionMode.Gateway + ? this.cosmosClientBuilder.WithConnectionModeGateway().Build() + : this.cosmosClientBuilder.Build()); + + // Making sure client telemetry is enabled + Assert.IsNotNull(this.GetClient().DocumentClient.clientTelemetry); + + this.database = await this.GetClient().CreateDatabaseAsync(Guid.NewGuid().ToString()); + + return await this.database.CreateContainerAsync( + id: Guid.NewGuid().ToString(), + partitionKeyPath: "/id", + throughput: isLargeContainer? 15000 : 400); + + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs index cedef0658a..9b78eb573c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs @@ -19,6 +19,8 @@ public HttpClientHandlerHelper() : base(new HttpClientHandler()) public Func> ResponseIntercepter { get; set; } + public Action ExceptionIntercepter { get; set; } + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpResponseMessage httpResponse = null; @@ -39,7 +41,19 @@ protected override async Task SendAsync(HttpRequestMessage } } - httpResponse = await base.SendAsync(request, cancellationToken); + try + { + httpResponse = await base.SendAsync(request, cancellationToken); + } + catch (Exception ex) { + + if (this.ExceptionIntercepter == null) + { + throw; + } + this.ExceptionIntercepter.Invoke(request, ex); + } + if (this.ResponseIntercepter != null) { httpResponse = await this.ResponseIntercepter(httpResponse); diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 510bf5f6f8..81cd3d3d91 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -7,6 +7,8 @@ variables: VmImage: windows-latest # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops BuildConfiguration: Release Packaging.EnableSBOMSigning: true + ReleasePackage: true + OS: 'Windows' stages: - stage: @@ -22,14 +24,45 @@ stages: BuildConfiguration: $(BuildConfiguration) Arguments: $(ReleaseArguments) VmImage: $(VmImage) + + - job: + displayName: TelemetryToService $(BuildConfiguration) + timeoutInMinutes: 120 + condition: and(succeeded(), eq('$(OS)', 'Windows')) + pool: + vmImage: windows-2019 + + steps: + - checkout: self # self represents the repo where the initial Pipelines YAML file was found + clean: true # if true, execute `execute git clean -ffdx && git reset --hard HEAD` before fetching + + # Add this Command to Include the .NET 6 SDK + - task: UseDotNet@2 + displayName: Use .NET 6.0 + inputs: + packageType: 'sdk' + version: '6.x' + + - task: DotNetCoreCLI@2 + displayName: Integration Test With Client Telemetry Service + condition: succeeded() + inputs: + command: test + projects: 'Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/*.csproj' + arguments: --filter "TestCategory=ClientTelemetryRelease" --verbosity normal --configuration $(BuildConfiguration) /p:OS=$(OS) + nugetConfigPath: NuGet.config + publishTestResults: true + testRunTitle: Microsoft.Azure.Cosmos.EmulatorTests + env: + COSMOSDB_ACCOUNT_CONNECTION_STRING: $(COSMOSDB_ACCOUNT_CONNECTION_STRING) # Real Account Connection String used by Integration Tests while running as part of release pipeline - stage: displayName: Publish jobs: - template: templates/nuget-pack.yml parameters: - BuildConfiguration: Release + BuildConfiguration: $(BuildConfiguration) VmImage: $(VmImage) - ReleasePackage: true + ReleasePackage: $(ReleasePackage) OutputPath: '$(Build.ArtifactStagingDirectory)/bin/AnyCPU/$(BuildConfiguration)/Microsoft.Azure.Cosmos' BlobVersion: $(BlobVersion) \ No newline at end of file diff --git a/templates/build-test.yml b/templates/build-test.yml index 3a016080e1..8d22aeb758 100644 --- a/templates/build-test.yml +++ b/templates/build-test.yml @@ -5,9 +5,9 @@ parameters: Arguments: '' VmImage: '' # https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops OS: 'Windows' - EmulatorPipeline1Arguments: ' --filter "TestCategory!=Quarantine & TestCategory!=Functional & (TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed)" --verbosity normal ' - EmulatorPipeline2Arguments: ' --filter "TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed" --verbosity normal ' - EmulatorPipeline1CategoryListName: ' Query, ChangeFeed, ReadFeed, Batch ' # Divided in 2 categories to run them in parallel and reduce the PR feedback time + EmulatorPipeline1Arguments: ' --filter "TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & (TestCategory=ClientTelemetryEmulator|TestCategory=Query|TestCategory=ReadFeed|TestCategory=Batch|TestCategory=ChangeFeed)" --verbosity normal ' + EmulatorPipeline2Arguments: ' --filter "TestCategory!=Quarantine & TestCategory!=Functional & TestCategory!=ClientTelemetryRelease & TestCategory!=ClientTelemetryEmulator & TestCategory!=Query & TestCategory!=ReadFeed & TestCategory!=Batch & TestCategory!=ChangeFeed" --verbosity normal ' + EmulatorPipeline1CategoryListName: ' Client Telemetry, Query, ChangeFeed, ReadFeed, Batch ' # Divided in 2 categories to run them in parallel and reduce the PR feedback time EmulatorPipeline2CategoryListName: ' Others ' jobs: From e2311a9fdcca392ec7d49c13939aaff3404deb85 Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Tue, 22 Aug 2023 22:55:01 +0530 Subject: [PATCH 16/20] [Internal] Client Telemetry: Refactors code for collectors (#4037) * refactored code * implemented review comments * test fix * fix tests * fix test * fix test * logger fix * update contract * fic test * updated benchmarks --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 67 +- .../src/Fluent/CosmosClientBuilder.cs | 1 - .../src/Handler/ClientPipelineBuilder.cs | 9 +- .../src/Handler/TelemetryHandler.cs | 38 +- .../src/Resource/ClientContextCore.cs | 4 +- .../src/Routing/ClientCollectionCache.cs | 42 +- .../src/Telemetry/ClientTelemetry.cs | 143 +-- .../Collector/ITelemetryCollector.cs | 25 + .../Telemetry/Collector/TelemetryCollector.cs | 77 ++ .../Collector/TelemetryCollectorNoOp.cs | 21 + .../Collector/TelemetryInformation.cs | 30 + .../src/Telemetry/TelemetryToServiceHelper.cs | 134 ++ .../src/Telemetry/VmMetadataApiHandler.cs | 6 +- ...iterBaselineTests.BatchOperationsAsync.xml | 37 +- ...riterBaselineTests.BulkOperationsAsync.xml | 974 ++++++++------- ...aceWriterBaselineTests.ChangeFeedAsync.xml | 888 ++++++++------ ...eWriterBaselineTests.MiscellanousAsync.xml | 86 +- ...neTests.PointOperationsExceptionsAsync.xml | 716 ++++++----- ...EndTraceWriterBaselineTests.QueryAsync.xml | 1079 ++++++++++------- ...TraceWriterBaselineTests.ReadFeedAsync.xml | 592 +++++---- ...TraceWriterBaselineTests.ReadManyAsync.xml | 222 ++-- ...selineTests.StreamPointOperationsAsync.xml | 148 ++- ...aselineTests.TypedPointOperationsAsync.xml | 148 ++- .../ClientTelemetryTestsBase.cs | 2 +- .../Contracts/BenchmarkResults.json | 2 +- .../Mocks/MockDocumentClient.cs | 33 +- .../HandlerTests.cs | 24 +- .../Utils/MockCosmosUtil.cs | 15 +- .../Utils/MockDocumentClient.cs | 17 +- 29 files changed, 3280 insertions(+), 2300 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/src/Telemetry/Collector/ITelemetryCollector.cs create mode 100644 Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollector.cs create mode 100644 Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollectorNoOp.cs create mode 100644 Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryInformation.cs create mode 100644 Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index c7aeb07a6f..9dee309028 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -142,7 +142,8 @@ internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider private Documents.ConsistencyLevel? desiredConsistencyLevel; internal CosmosAccountServiceConfiguration accountServiceConfiguration { get; private set; } - internal ClientTelemetry clientTelemetry { get; set; } + + internal TelemetryToServiceHelper telemetryToServiceHelper { get; set; } private ClientCollectionCache collectionCache; @@ -564,11 +565,11 @@ public DocumentClient(Uri serviceEndpoint, /// /// Internal constructor purely for unit-testing /// - internal DocumentClient(Uri serviceEndpoint, string authKey) + internal DocumentClient(Uri serviceEndpoint, ConnectionPolicy connectionPolicy) { // do nothing this.ServiceEndpoint = serviceEndpoint; - this.ConnectionPolicy = new ConnectionPolicy(); + this.ConnectionPolicy = connectionPolicy ?? new ConnectionPolicy(); } internal virtual async Task GetCollectionCacheAsync(ITrace trace) @@ -660,7 +661,7 @@ private async Task OpenPrivateAsync(CancellationToken cancellationToken) storeModel: this.GatewayStoreModel, tokenProvider: this, retryPolicy: this.retryPolicy, - clientTelemetry: this.clientTelemetry); + telemetryToServiceHelper: this.telemetryToServiceHelper); this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache); DefaultTrace.TraceWarning("{0} occurred while OpenAsync. Exception Message: {1}", ex.ToString(), ex.Message); @@ -939,6 +940,15 @@ internal virtual void Initialize(Uri serviceEndpoint, // Loading VM Information (non blocking call and initialization won't fail if this call fails) VmMetadataApiHandler.TryInitialize(this.httpClient); + // Starting ClientTelemetry Job + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob(this.clientId, + this.ConnectionPolicy, + this.cosmosAuthorization, + this.httpClient, + this.ServiceEndpoint, + this.GlobalEndpointManager, + this.cancellationTokenSource); + if (sessionContainer != null) { this.sessionContainer = sessionContainer; @@ -961,12 +971,6 @@ internal virtual void Initialize(Uri serviceEndpoint, // For direct: WFStoreProxy [set in OpenAsync()]. this.eventSource = DocumentClientEventSource.Instance; - // Disable system usage for internal builds. Cosmos DB owns the VMs and already logs - // the system information so no need to track it. -#if !INTERNAL - this.InitializeClientTelemetry(); -#endif - this.initializeTaskFactory = (_) => TaskHelper.InlineIfPossible( () => this.GetInitializationTaskAsync(storeClientFactory: storeClientFactory), new ResourceThrottleRetryPolicy( @@ -1028,7 +1032,7 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli storeModel: this.GatewayStoreModel, tokenProvider: this, retryPolicy: this.retryPolicy, - clientTelemetry: this.clientTelemetry); + telemetryToServiceHelper: this.telemetryToServiceHelper); this.partitionKeyRangeCache = new PartitionKeyRangeCache(this, this.GatewayStoreModel, this.collectionCache); this.ResetSessionTokenRetryPolicy = new ResetSessionTokenRetryPolicyFactory(this.sessionContainer, this.collectionCache, this.retryPolicy); @@ -1046,36 +1050,6 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli return true; } - private void InitializeClientTelemetry() - { - if (this.ConnectionPolicy.EnableClientTelemetry) - { - try - { - this.clientTelemetry = ClientTelemetry.CreateAndStartBackgroundTelemetry( - clientId: this.clientId, - httpClient: this.httpClient, - userAgent: this.ConnectionPolicy.UserAgentContainer.BaseUserAgent, - connectionMode: this.ConnectionPolicy.ConnectionMode, - authorizationTokenProvider: this.cosmosAuthorization, - diagnosticsHelper: DiagnosticsHandlerHelper.Instance, - preferredRegions: this.ConnectionPolicy.PreferredLocations, - globalEndpointManager: this.GlobalEndpointManager); - - DefaultTrace.TraceInformation("Client Telemetry Enabled."); - } - catch (Exception ex) - { - DefaultTrace.TraceInformation($"Error While starting Telemetry Job : {ex.Message}. Hence disabling Client Telemetry"); - this.ConnectionPolicy.EnableClientTelemetry = false; - } - } - else - { - DefaultTrace.TraceInformation("Client Telemetry Disabled."); - } - } - private async Task InitializeCachesAsync(string databaseName, DocumentCollection collection, CancellationToken cancellationToken) { if (databaseName == null) @@ -1279,6 +1253,12 @@ public void Dispose() return; } + if (this.telemetryToServiceHelper != null) + { + this.telemetryToServiceHelper.Dispose(); + this.telemetryToServiceHelper = null; + } + if (!this.cancellationTokenSource.IsCancellationRequested) { this.cancellationTokenSource.Cancel(); @@ -1346,11 +1326,6 @@ public void Dispose() this.initTaskCache = null; } - if (this.clientTelemetry != null) - { - this.clientTelemetry.Dispose(); - } - DefaultTrace.TraceInformation("DocumentClient with id {0} disposed.", this.traceId); DefaultTrace.Flush(); diff --git a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs index 9c8c2fbb4e..bf04d103bb 100644 --- a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs +++ b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs @@ -15,7 +15,6 @@ namespace Microsoft.Azure.Cosmos.Fluent using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Client; - using Telemetry; /// /// This is a Builder class that creates a cosmos client diff --git a/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs b/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs index d4c260f904..aa69b770e1 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs @@ -27,7 +27,7 @@ public ClientPipelineBuilder( CosmosClient client, ConsistencyLevel? requestedClientConsistencyLevel, IReadOnlyCollection customHandlers, - ClientTelemetry telemetry) + TelemetryToServiceHelper telemetryToServiceHelper) { this.client = client ?? throw new ArgumentNullException(nameof(client)); this.requestedClientConsistencyLevel = requestedClientConsistencyLevel; @@ -48,11 +48,8 @@ public ClientPipelineBuilder( #else this.diagnosticsHandler = null; #endif - if (telemetry != null) - { - this.telemetryHandler = new TelemetryHandler(telemetry); - Debug.Assert(this.telemetryHandler.InnerHandler == null, nameof(this.telemetryHandler)); - } + this.telemetryHandler = new TelemetryHandler(telemetryToServiceHelper); + Debug.Assert(this.telemetryHandler.InnerHandler == null, nameof(this.telemetryHandler)); this.UseRetryPolicy(); this.AddCustomHandlers(customHandlers); diff --git a/Microsoft.Azure.Cosmos/src/Handler/TelemetryHandler.cs b/Microsoft.Azure.Cosmos/src/Handler/TelemetryHandler.cs index 9d7e504eba..039373718f 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/TelemetryHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/TelemetryHandler.cs @@ -10,14 +10,15 @@ namespace Microsoft.Azure.Cosmos.Handlers using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Telemetry.Collector; internal class TelemetryHandler : RequestHandler { - private readonly ClientTelemetry telemetry; + private readonly TelemetryToServiceHelper telemetryToServiceHelper; - public TelemetryHandler(ClientTelemetry telemetry) + public TelemetryHandler(TelemetryToServiceHelper telemetryToServiceHelper) { - this.telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry)); + this.telemetryToServiceHelper = telemetryToServiceHelper ?? throw new ArgumentNullException(nameof(telemetryToServiceHelper)); } public override async Task SendAsync( @@ -29,19 +30,22 @@ public override async Task SendAsync( { try { - this.telemetry - .CollectOperationInfo( - cosmosDiagnostics: response.Diagnostics, - statusCode: response.StatusCode, - responseSizeInBytes: this.GetPayloadSize(response), - containerId: request.ContainerId, - databaseId: request.DatabaseId, - operationType: request.OperationType, - resourceType: request.ResourceType, - consistencyLevel: request.Headers?[Documents.HttpConstants.HttpHeaders.ConsistencyLevel], - requestCharge: response.Headers.RequestCharge, - subStatusCode: response.Headers.SubStatusCode, - trace: response.Trace); + this.telemetryToServiceHelper.GetCollector().CollectOperationAndNetworkInfo( + () => new TelemetryInformation + { + RegionsContactedList = response.Diagnostics.GetContactedRegions(), + RequestLatency = response.Diagnostics.GetClientElapsedTime(), + StatusCode = response.StatusCode, + ResponseSizeInBytes = TelemetryHandler.GetPayloadSize(response), + ContainerId = request.ContainerId, + DatabaseId = request.DatabaseId, + OperationType = request.OperationType, + ResourceType = request.ResourceType, + ConsistencyLevel = request.Headers?[Documents.HttpConstants.HttpHeaders.ConsistencyLevel], + RequestCharge = response.Headers.RequestCharge, + SubStatusCode = response.Headers.SubStatusCode, + Trace = response.Trace + }); } catch (Exception ex) { @@ -63,7 +67,7 @@ private bool IsAllowed(RequestMessage request) /// /// /// Size of Payload - private long GetPayloadSize(ResponseMessage response) + private static long GetPayloadSize(ResponseMessage response) { if (response != null) { diff --git a/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs b/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs index 42e128ac2e..a128e178d0 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs @@ -13,8 +13,6 @@ namespace Microsoft.Azure.Cosmos using System.Text; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Handler; using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Cosmos.Resource.CosmosExceptions; using Microsoft.Azure.Cosmos.Routing; @@ -122,7 +120,7 @@ internal static CosmosClientContext Create( cosmosClient, clientOptions.ConsistencyLevel, clientOptions.CustomHandlers, - telemetry: documentClient.clientTelemetry); + telemetryToServiceHelper: documentClient.telemetryToServiceHelper); requestInvokerHandler = clientPipelineBuilder.Build(); } diff --git a/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs b/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs index 530bbfceb5..534a0b8a22 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos.Routing using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Common; using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Telemetry.Collector; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Cosmos.Tracing.TraceData; using Microsoft.Azure.Documents; @@ -25,20 +26,20 @@ internal class ClientCollectionCache : CollectionCache private readonly ICosmosAuthorizationTokenProvider tokenProvider; private readonly IRetryPolicyFactory retryPolicy; private readonly ISessionContainer sessionContainer; - private readonly ClientTelemetry clientTelemetry; + private readonly TelemetryToServiceHelper telemetryToServiceHelper; public ClientCollectionCache( ISessionContainer sessionContainer, IStoreModel storeModel, ICosmosAuthorizationTokenProvider tokenProvider, IRetryPolicyFactory retryPolicy, - ClientTelemetry clientTelemetry) + TelemetryToServiceHelper telemetryToServiceHelper) { this.storeModel = storeModel ?? throw new ArgumentNullException("storeModel"); this.tokenProvider = tokenProvider; this.retryPolicy = retryPolicy; this.sessionContainer = sessionContainer; - this.clientTelemetry = clientTelemetry; + this.telemetryToServiceHelper = telemetryToServiceHelper; } protected override Task GetByRidAsync(string apiVersion, @@ -214,21 +215,19 @@ private async Task ReadCollectionAsync( await this.storeModel.ProcessMessageAsync(request)) { ContainerProperties containerProperties = CosmosResource.FromStream(response); - - if (this.clientTelemetry != null) - { - ClientCollectionCache.GetDatabaseAndCollectionName(collectionLink, out string databaseName, out string collectionName); - this.clientTelemetry.CollectCacheInfo( - cacheRefreshSource: ClientCollectionCache.TelemetrySourceName, - regionsContactedList: response.RequestStats.RegionsContacted, - requestLatency: response.RequestStats.RequestLatency, - statusCode: response.StatusCode, - containerId: collectionName, - operationType: request.OperationType, - resourceType: request.ResourceType, - subStatusCode: response.SubStatusCode, - databaseId: databaseName); - } + + this.telemetryToServiceHelper.GetCollector().CollectCacheInfo( + ClientCollectionCache.TelemetrySourceName, + () => new TelemetryInformation + { + RegionsContactedList = response.RequestStats.RegionsContacted, + RequestLatency = response.RequestStats.RequestLatency, + StatusCode = response.StatusCode, + OperationType = request.OperationType, + ResourceType = request.ResourceType, + SubStatusCode = response.SubStatusCode, + CollectionLink = collectionLink + }); return containerProperties; } @@ -242,12 +241,5 @@ await this.storeModel.ProcessMessageAsync(request)) } } } - - private static void GetDatabaseAndCollectionName(string path, out string databaseName, out string collectionName) - { - string[] segments = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - - PathsHelper.ParseDatabaseNameAndCollectionNameFromUrlSegments(segments, out databaseName, out collectionName); - } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs index dde7d5a7c5..2dfcbab496 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs @@ -7,18 +7,16 @@ namespace Microsoft.Azure.Cosmos.Telemetry using System; using System.Collections.Concurrent; using System.Collections.Generic; - using System.Net; using System.Threading; using System.Threading.Tasks; using Handler; using HdrHistogram; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry.Collector; using Microsoft.Azure.Cosmos.Telemetry.Models; - using Microsoft.Azure.Cosmos.Tracing; - using Microsoft.Azure.Cosmos.Tracing.TraceData; - using Microsoft.Azure.Documents; using Util; + using static Microsoft.Azure.Cosmos.Tracing.TraceData.ClientSideRequestStatisticsTraceDatum; /// /// This class collects and send all the telemetry information. @@ -215,9 +213,9 @@ internal static async Task RunProcessorTaskAsync(string telemetryDate, Task proc Task resultTask = await Task.WhenAny(processingTask, delayTask); if (resultTask == delayTask) { - DefaultTrace.TraceVerbose($"Processor task with date as {telemetryDate} is canceled as it did not finish in {timeout}"); + DefaultTrace.TraceVerbose($"Processor task with date as {0} is canceled as it did not finish in {1}", telemetryDate, timeout); // Operation cancelled - throw new OperationCanceledException($"Processor task with date as {telemetryDate} is canceled as it did not finish in {timeout}"); + throw new OperationCanceledException(string.Format($"Processor task with date as {0} is canceled as it did not finish in {1}", telemetryDate, timeout)); } else { @@ -230,38 +228,36 @@ internal static async Task RunProcessorTaskAsync(string telemetryDate, Task proc /// /// Collects Cache Telemetry Information. /// - internal void CollectCacheInfo(string cacheRefreshSource, - HashSet<(string regionName, Uri uri)> regionsContactedList, - TimeSpan? requestLatency, - HttpStatusCode statusCode, - string containerId, - OperationType operationType, - ResourceType resourceType, - SubStatusCodes subStatusCode, - string databaseId, - long responseSizeInBytes = 0, - string consistencyLevel = null ) + internal void PushCacheDatapoint(string cacheName, TelemetryInformation data) { - if (string.IsNullOrEmpty(cacheRefreshSource)) + if (string.IsNullOrEmpty(cacheName)) { - throw new ArgumentNullException(nameof(cacheRefreshSource)); + throw new ArgumentNullException(nameof(cacheName)); } - DefaultTrace.TraceVerbose($"Collecting cacheRefreshSource {0} data for Telemetry.", cacheRefreshSource); + // If latency information is not available. Ignore this datapoint. It is not expected but putting this safety check + if (!data.RequestLatency.HasValue) + { + DefaultTrace.TraceWarning($"Latency data point is not available for {0} cache call", cacheName); + + return; + } + + DefaultTrace.TraceVerbose($"Collecting cacheRefreshSource {0} data for Telemetry.", cacheName); - string regionsContacted = ClientTelemetryHelper.GetContactedRegions(regionsContactedList); + string regionsContacted = ClientTelemetryHelper.GetContactedRegions(data.RegionsContactedList); // Recording Request Latency - CacheRefreshInfo payloadKey = new CacheRefreshInfo(cacheRefreshSource: cacheRefreshSource, + CacheRefreshInfo payloadKey = new CacheRefreshInfo(cacheRefreshSource: cacheName, regionsContacted: regionsContacted?.ToString(), - responseSizeInBytes: responseSizeInBytes, - consistency: consistencyLevel, - databaseName: databaseId, - containerName: containerId, - operation: operationType, - resource: resourceType, - statusCode: (int)statusCode, - subStatusCode: (int)subStatusCode); + responseSizeInBytes: data.ResponseSizeInBytes, + consistency: data.ConsistencyLevel, + databaseName: data.DatabaseId, + containerName: data.ContainerId, + operation: data.OperationType, + resource: data.ResourceType, + statusCode: (int)data.StatusCode, + subStatusCode: (int)data.SubStatusCode); LongConcurrentHistogram latency = this.cacheRefreshInfoMap .GetOrAdd(payloadKey, new LongConcurrentHistogram(ClientTelemetryOptions.RequestLatencyMin, @@ -269,7 +265,7 @@ internal void CollectCacheInfo(string cacheRefreshSource, ClientTelemetryOptions.RequestLatencyPrecision)); try { - latency.RecordValue(requestLatency.Value.Ticks); + latency.RecordValue(data.RequestLatency.Value.Ticks); } catch (Exception ex) { @@ -280,52 +276,22 @@ internal void CollectCacheInfo(string cacheRefreshSource, /// /// Collects Telemetry Information. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal void CollectOperationInfo(CosmosDiagnostics cosmosDiagnostics, - HttpStatusCode statusCode, - long responseSizeInBytes, - string containerId, - string databaseId, - OperationType operationType, - ResourceType resourceType, - string consistencyLevel, - double requestCharge, - SubStatusCodes subStatusCode, - ITrace trace) + internal void PushOperationDatapoint(TelemetryInformation data) { DefaultTrace.TraceVerbose("Collecting Operation data for Telemetry."); - if (cosmosDiagnostics == null) - { - throw new ArgumentNullException(nameof(cosmosDiagnostics)); - } - - // Record Network/Replica Information - SummaryDiagnostics summaryDiagnostics = new SummaryDiagnostics(trace); - this.networkDataRecorder.Record(summaryDiagnostics.StoreResponseStatistics.Value, databaseId, containerId); - - string regionsContacted = ClientTelemetryHelper.GetContactedRegions(cosmosDiagnostics.GetContactedRegions()); + string regionsContacted = ClientTelemetryHelper.GetContactedRegions(data.RegionsContactedList); // Recording Request Latency and Request Charge OperationInfo payloadKey = new OperationInfo(regionsContacted: regionsContacted?.ToString(), - responseSizeInBytes: responseSizeInBytes, - consistency: consistencyLevel, - databaseName: databaseId, - containerName: containerId, - operation: operationType, - resource: resourceType, - statusCode: (int)statusCode, - subStatusCode: (int)subStatusCode); + responseSizeInBytes: data.ResponseSizeInBytes, + consistency: data.ConsistencyLevel, + databaseName: data.DatabaseId, + containerName: data.ContainerId, + operation: data.OperationType, + resource: data.ResourceType, + statusCode: (int)data.StatusCode, + subStatusCode: (int)data.SubStatusCode); (LongConcurrentHistogram latency, LongConcurrentHistogram requestcharge) = this.operationInfoMap .GetOrAdd(payloadKey, x => (latency: new LongConcurrentHistogram(ClientTelemetryOptions.RequestLatencyMin, @@ -334,16 +300,26 @@ internal void CollectOperationInfo(CosmosDiagnostics cosmosDiagnostics, requestcharge: new LongConcurrentHistogram(ClientTelemetryOptions.RequestChargeMin, ClientTelemetryOptions.RequestChargeMax, ClientTelemetryOptions.RequestChargePrecision))); - try + + // If latency information is not available. Ignore this datapoint. It is not expected but putting this safety check + if (data.RequestLatency.HasValue) { - latency.RecordValue(cosmosDiagnostics.GetClientElapsedTime().Ticks); + try + { + latency.RecordValue(data.RequestLatency.Value.Ticks); + } + catch (Exception ex) + { + DefaultTrace.TraceError("Latency Recording Failed by Telemetry. Exception : {0}", ex); + } + } - catch (Exception ex) + else { - DefaultTrace.TraceError("Latency Recording Failed by Telemetry. Exception : {0}", ex); + DefaultTrace.TraceWarning($"Latency data point is not available for an operation"); } - - long requestChargeToRecord = (long)(requestCharge * ClientTelemetryOptions.HistogramPrecisionFactor); + + long requestChargeToRecord = (long)(data.RequestCharge * ClientTelemetryOptions.HistogramPrecisionFactor); try { requestcharge.RecordValue(requestChargeToRecord); @@ -354,6 +330,19 @@ internal void CollectOperationInfo(CosmosDiagnostics cosmosDiagnostics, } } + /// + /// Record Network Request Telemetry Information + /// + /// + /// + /// + public void PushNetworkDataPoint(List storeResponseStatistics, string databaseId, string containerId) + { + // Record Network/Replica Information + this.networkDataRecorder.Record(storeResponseStatistics, databaseId, containerId); + + } + /// /// Dispose of cosmos client.It will get disposed with client so not making it thread safe. /// diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/Collector/ITelemetryCollector.cs b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/ITelemetryCollector.cs new file mode 100644 index 0000000000..aef7a5efc4 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/ITelemetryCollector.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Telemetry +{ + using System; + using Microsoft.Azure.Cosmos.Telemetry.Collector; + + internal interface ITelemetryCollector + { + /// + /// Collect information required to collect the telemetry information for the cache. + /// + /// Cache Name + /// delegate that encapsulates a method that returns a CacheTelemetryInformation object + public void CollectCacheInfo(string cacheName, Func functionFordata); + + /// + /// Collect information required to collect the telemetry information for the operation. + /// + /// delegate that encapsulates a method that returns a OperationTelemetryInformation object + public void CollectOperationAndNetworkInfo(Func functionFordata); + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollector.cs b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollector.cs new file mode 100644 index 0000000000..9570fcd56a --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollector.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Telemetry.Collector +{ + using System; + using Microsoft.Azure.Cosmos; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Telemetry; + using Microsoft.Azure.Cosmos.Tracing.TraceData; + using Microsoft.Azure.Documents; + + internal class TelemetryCollector : ITelemetryCollector + { + private readonly ClientTelemetry clientTelemetry = null; + private readonly ConnectionPolicy connectionPolicy = null; + + internal TelemetryCollector( + ClientTelemetry clientTelemetry, + ConnectionPolicy connectionPolicy) + { + this.clientTelemetry = clientTelemetry; + this.connectionPolicy = connectionPolicy; + } + + public void CollectCacheInfo(string cacheName, Func functionFordata) + { + try + { + TelemetryInformation data = functionFordata(); + + if (data.CollectionLink != null) + { + GetDatabaseAndCollectionName(data.CollectionLink, out string databaseName, out string collectionName); + + data.DatabaseId = databaseName; + data.ContainerId = collectionName; + } + + this.clientTelemetry?.PushCacheDatapoint(cacheName, data); + } + catch (Exception ex) + { + DefaultTrace.TraceError($"Error while collecting cache {0} telemetry. Exception : {1}", cacheName, ex); + } + } + + public void CollectOperationAndNetworkInfo(Func functionFordata) + { + try + { + TelemetryInformation data = functionFordata(); + + this.clientTelemetry?.PushOperationDatapoint(data); + + // Collect network level telemetry only in Direct Mode + if (this.connectionPolicy.ConnectionMode == ConnectionMode.Direct) + { + SummaryDiagnostics summaryDiagnostics = new SummaryDiagnostics(data.Trace); + this.clientTelemetry?.PushNetworkDataPoint(summaryDiagnostics.StoreResponseStatistics.Value, data.DatabaseId, data.ContainerId); + } + } + catch (Exception ex) + { + DefaultTrace.TraceError($"Error while collecting operation telemetry. Exception : {1}", ex); + } + } + + private static void GetDatabaseAndCollectionName(string path, out string databaseName, out string collectionName) + { + string[] segments = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + PathsHelper.ParseDatabaseNameAndCollectionNameFromUrlSegments(segments, out databaseName, out collectionName); + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollectorNoOp.cs b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollectorNoOp.cs new file mode 100644 index 0000000000..a1d286fe94 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryCollectorNoOp.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Telemetry.Collector +{ + using System; + + internal class TelemetryCollectorNoOp : ITelemetryCollector + { + public void CollectCacheInfo(string cacheName, Func functionFordata) + { + //NoOps + } + + public void CollectOperationAndNetworkInfo(Func functionFordata) + { + //NoOps + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryInformation.cs b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryInformation.cs new file mode 100644 index 0000000000..a8210fb0a4 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/Collector/TelemetryInformation.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Telemetry.Collector +{ + using System; + using System.Collections.Generic; + using System.Net; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + + internal class TelemetryInformation + { + internal HttpStatusCode StatusCode { get; set; } + internal SubStatusCodes SubStatusCode { get; set; } + internal OperationType OperationType { get; set; } + internal ResourceType ResourceType { get; set; } + internal string ContainerId { get; set; } + internal string DatabaseId { get; set; } + internal long ResponseSizeInBytes { get; set; } = 0; + internal string ConsistencyLevel { get; set; } = null; + internal IReadOnlyCollection<(string regionName, Uri uri)> RegionsContactedList { get; set; } + internal TimeSpan? RequestLatency { get; set; } + + internal double RequestCharge { get; set; } // Required only for operation level telemetry + internal string CollectionLink { get; set; } = null; // Required only for collection cache telemetry + internal ITrace Trace { get; set; } // Required to fetch network level telemetry out of the trace object + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs b/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs new file mode 100644 index 0000000000..2f78df0913 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs @@ -0,0 +1,134 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Telemetry +{ + using System; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Handler; + using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry.Collector; + + internal class TelemetryToServiceHelper : IDisposable + { + private ITelemetryCollector collector = new TelemetryCollectorNoOp(); + + internal static int DefaultBackgroundRefreshClientConfigTimeIntervalInMS = (int)TimeSpan.FromMinutes(10).TotalMilliseconds; + + private readonly AuthorizationTokenProvider cosmosAuthorization; + private readonly CosmosHttpClient httpClient; + private readonly Uri serviceEnpoint; + private readonly ConnectionPolicy connectionPolicy; + private readonly string clientId; + private readonly GlobalEndpointManager globalEndpointManager; + private readonly CancellationTokenSource cancellationTokenSource; + + private ClientTelemetry clientTelemetry = null; + + private TelemetryToServiceHelper() + { + //NoOpConstructor + } + + private TelemetryToServiceHelper( + string clientId, + ConnectionPolicy connectionPolicy, + AuthorizationTokenProvider cosmosAuthorization, + CosmosHttpClient httpClient, + Uri serviceEndpoint, + GlobalEndpointManager globalEndpointManager, + CancellationTokenSource cancellationTokenSource) + { + this.clientId = clientId; + this.cosmosAuthorization = cosmosAuthorization; + this.httpClient = httpClient; + this.connectionPolicy = connectionPolicy; + this.serviceEnpoint = serviceEndpoint; + this.globalEndpointManager = globalEndpointManager; + this.cancellationTokenSource = cancellationTokenSource; + } + + public static TelemetryToServiceHelper CreateAndInitializeClientConfigAndTelemetryJob(string clientId, + ConnectionPolicy connectionPolicy, + AuthorizationTokenProvider cosmosAuthorization, + CosmosHttpClient httpClient, + Uri serviceEndpoint, + GlobalEndpointManager globalEndpointManager, + CancellationTokenSource cancellationTokenSource) + { +#if INTERNAL + return new TelemetryToServiceHelper(); +#else + if (!connectionPolicy.EnableClientTelemetry) + { + return new TelemetryToServiceHelper(); + } + + TelemetryToServiceHelper helper = new TelemetryToServiceHelper( + clientId, connectionPolicy, cosmosAuthorization, httpClient, serviceEndpoint, globalEndpointManager, cancellationTokenSource); + + helper.InitializeClientTelemetry(); + + return helper; +#endif + } + + public ITelemetryCollector GetCollector() + { + return this.collector; + } + + public bool IsClientTelemetryJobRunning() + { + return this.clientTelemetry != null; + } + + /// + /// Trigger Client Telemetry job when it is enabled and not already running. + /// + private void InitializeClientTelemetry() + { + try + { + this.clientTelemetry = ClientTelemetry.CreateAndStartBackgroundTelemetry( + clientId: this.clientId, + httpClient: this.httpClient, + userAgent: this.connectionPolicy.UserAgentContainer.BaseUserAgent, + connectionMode: this.connectionPolicy.ConnectionMode, + authorizationTokenProvider: this.cosmosAuthorization, + diagnosticsHelper: DiagnosticsHandlerHelper.Instance, + preferredRegions: this.connectionPolicy.PreferredLocations, + globalEndpointManager: this.globalEndpointManager); + + this.collector = new TelemetryCollector(this.clientTelemetry, this.connectionPolicy); + + DefaultTrace.TraceVerbose("Client Telemetry Enabled."); + } + catch (Exception ex) + { + DefaultTrace.TraceWarning($"Error While starting Telemetry Job : {0}. Hence disabling Client Telemetry", ex.Message); + this.connectionPolicy.EnableClientTelemetry = false; + } + } + + public void Dispose() + { + this.StopClientTelemetry(); + } + + /// + /// Stopping a client telemetry job means now there shouldn't be any valid collector available, Hence switch it to NoOp collector. + /// Along with it, send a signal to stop client telemetry job. + /// + private void StopClientTelemetry() + { + this.collector = new TelemetryCollectorNoOp(); + + this.clientTelemetry?.Dispose(); + this.clientTelemetry = null; + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/VmMetadataApiHandler.cs b/Microsoft.Azure.Cosmos/src/Telemetry/VmMetadataApiHandler.cs index ed5ad111d7..b9d9c60d36 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/VmMetadataApiHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/VmMetadataApiHandler.cs @@ -85,11 +85,11 @@ static ValueTask CreateRequestMessage() azMetadata = await VmMetadataApiHandler.ProcessResponseAsync(response); - DefaultTrace.TraceInformation($"Succesfully get Instance Metadata Response : {azMetadata.Compute.VMId}"); + DefaultTrace.TraceInformation($"Succesfully get Instance Metadata Response : {0}", azMetadata.Compute.VMId); } catch (Exception e) { - DefaultTrace.TraceInformation($"Azure Environment metadata information not available. {e.Message}"); + DefaultTrace.TraceInformation($"Azure Environment metadata information not available. {0}", e.Message); } } @@ -154,7 +154,7 @@ internal static string GetCloudInformation() } catch (Exception ex) { - DefaultTrace.TraceWarning("Error while generating hashed machine name " + ex.Message); + DefaultTrace.TraceWarning($"Error while generating hashed machine name {0}", ex.Message); } return $"{VmMetadataApiHandler.UuidPrefix}{Guid.NewGuid()}"; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.BatchOperationsAsync.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.BatchOperationsAsync.xml index fd097032d9..fb6a5fb720 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.BatchOperationsAsync.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.BatchOperationsAsync.xml @@ -50,14 +50,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds ]]> @@ -116,23 +117,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -454,14 +461,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -503,23 +511,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -841,14 +855,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -890,23 +905,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1228,14 +1249,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -1277,23 +1299,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1615,14 +1643,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -1664,23 +1693,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2002,14 +2037,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -2051,23 +2087,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2389,14 +2431,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -2438,23 +2481,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2776,14 +2825,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -2825,23 +2875,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3163,14 +3219,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -3212,23 +3269,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3550,14 +3613,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds └── Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -3599,23 +3663,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3959,35 +4029,36 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds ├── Batch Retry Async(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds │ └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds @@ -3999,35 +4070,36 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds ├── Batch Retry Async(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds │ └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds @@ -4039,35 +4111,36 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds ├── Batch Retry Async(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds │ └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds @@ -4079,35 +4152,36 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) - │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) - │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) + │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) + │ ├── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) └── Create Trace(00000000-0000-0000-0000-000000000000) Batch-Component 00:00:00:000 0.00 milliseconds ]]> @@ -369,23 +377,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -459,23 +473,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -547,23 +567,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -635,23 +661,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -723,23 +755,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -791,23 +829,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -855,23 +899,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -919,23 +969,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1165,14 +1221,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -1195,14 +1252,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -1225,14 +1283,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -1255,14 +1314,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -1391,23 +1451,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1485,23 +1551,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1577,23 +1649,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1669,23 +1747,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1918,14 +2002,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds ├── Change Feed Iterator Read Next Async(00000000-0000-0000-0000-000000000000) ChangeFeed-Component 00:00:00:000 0.00 milliseconds @@ -1947,14 +2032,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds ├── Change Feed Iterator Read Next Async(00000000-0000-0000-0000-000000000000) ChangeFeed-Component 00:00:00:000 0.00 milliseconds @@ -1976,14 +2062,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds └── Change Feed Iterator Read Next Async(00000000-0000-0000-0000-000000000000) ChangeFeed-Component 00:00:00:000 0.00 milliseconds @@ -2005,14 +2092,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds └── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds ]]> @@ -2140,23 +2228,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2230,23 +2324,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2318,23 +2418,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2406,23 +2512,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2651,14 +2763,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -2681,14 +2794,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -2711,14 +2825,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -2741,14 +2856,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── Get RID(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds ├── Get Collection Cache(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds └── ChangeFeed Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -2877,23 +2993,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2971,23 +3093,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3063,23 +3191,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3155,23 +3289,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3379,56 +3519,60 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds │ └── Microsoft.Azure.Cosmos.Handlers.DiagnosticsHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds │ │ ( │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds │ └── Microsoft.Azure.Cosmos.Handlers.DiagnosticsHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds │ │ ( │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) └── Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds └── Microsoft.Azure.Cosmos.Handlers.DiagnosticsHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds │ ( │ [System Info] │ Redacted To Not Change The Baselines From Run To Run │ ) - └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - ( - [Client Side Request Stats] - Redacted To Not Change The Baselines From Run To Run - ) + └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + ( + [Client Side Request Stats] + Redacted To Not Change The Baselines From Run To Run + ) ]]> @@ -958,23 +990,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1056,23 +1094,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1154,23 +1198,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1252,23 +1302,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -1461,14 +1517,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds ├── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ │ ( @@ -1496,14 +1553,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds ├── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ │ ( @@ -1531,14 +1589,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds └── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ ( @@ -1566,14 +1625,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds ]]> @@ -2366,23 +2454,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2464,23 +2558,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2562,23 +2662,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2660,23 +2766,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -2862,16 +2974,17 @@ │ │ │ │ [System Info] │ │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ │ ) - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.GatewayStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ [PointOperationStatisticsTraceDatum] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.GatewayStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ [PointOperationStatisticsTraceDatum] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) │ │ └── Get Partition Key Ranges(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ │ └── Try Get Overlapping Ranges(00000000-0000-0000-0000-000000000000) Routing-Component 00:00:00:000 0.00 milliseconds │ ├── MoveNextAsync(00000000-0000-0000-0000-000000000000) Pagination-Component 00:00:00:000 0.00 milliseconds @@ -2892,14 +3005,15 @@ │ │ │ │ [System Info] │ │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ │ ) - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) │ │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds │ └── Query Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ├── Typed FeedIterator ReadNextAsync(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -2928,14 +3042,15 @@ │ │ │ │ [System Info] │ │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ │ ) - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) │ │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds │ └── Query Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ├── Typed FeedIterator ReadNextAsync(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -2964,14 +3079,15 @@ │ │ │ │ [System Info] │ │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ │ ) - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ │ ( - │ │ │ [Client Side Request Stats] - │ │ │ Redacted To Not Change The Baselines From Run To Run - │ │ │ ) + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ │ ( + │ │ │ [Client Side Request Stats] + │ │ │ Redacted To Not Change The Baselines From Run To Run + │ │ │ ) │ │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds │ └── Query Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds └── Typed FeedIterator ReadNextAsync(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds @@ -3000,14 +3116,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds └── Query Response Serialization(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds ]]> @@ -3059,24 +3176,30 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.GatewayStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run", - "PointOperationStatisticsTraceDatum": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Cosmos.GatewayStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run", + "PointOperationStatisticsTraceDatum": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3147,23 +3270,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3245,23 +3374,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3343,23 +3478,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3441,23 +3582,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -3653,14 +3800,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds ├── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ │ ( @@ -3688,14 +3836,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds ├── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ │ ( @@ -3723,14 +3872,15 @@ │ │ │ [System Info] │ │ │ Redacted To Not Change The Baselines From Run To Run │ │ │ ) - │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ │ ( - │ │ [Client Side Request Stats] - │ │ Redacted To Not Change The Baselines From Run To Run - │ │ ) + │ │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ │ ( + │ │ [Client Side Request Stats] + │ │ Redacted To Not Change The Baselines From Run To Run + │ │ ) │ └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds └── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ ( @@ -3758,14 +3908,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) └── Get Cosmos Element Response(00000000-0000-0000-0000-000000000000) Json-Component 00:00:00:000 0.00 milliseconds ]]> @@ -4573,23 +4752,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -4671,23 +4856,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -4769,23 +4960,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } @@ -4867,23 +5064,29 @@ }, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.TelemetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RetryHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", + "name": "Microsoft.Azure.Cosmos.Handlers.RouterHandler", "duration in milliseconds": 0, "children": [ { - "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "name": "Microsoft.Azure.Cosmos.Handlers.TransportHandler", "duration in milliseconds": 0, - "data": { - "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" - } + "children": [ + { + "name": "Microsoft.Azure.Documents.ServerStoreModel Transport Request", + "duration in milliseconds": 0, + "data": { + "Client Side Request Stats": "Redacted To Not Change The Baselines From Run To Run" + } + } + ] } ] } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.ReadFeedAsync.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.ReadFeedAsync.xml index 86f16841b5..60f1b42df3 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.ReadFeedAsync.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.ReadFeedAsync.xml @@ -54,14 +54,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ │ ( │ │ [Client Configuration] @@ -81,14 +82,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) ├── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ │ ( │ │ [Client Configuration] @@ -108,14 +110,15 @@ │ │ [System Info] │ │ Redacted To Not Change The Baselines From Run To Run │ │ ) - │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - │ ( - │ [Client Side Request Stats] - │ Redacted To Not Change The Baselines From Run To Run - │ ) + │ └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + │ └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + │ ( + │ [Client Side Request Stats] + │ Redacted To Not Change The Baselines From Run To Run + │ ) └── FeedIterator Read Next Async(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds │ ( │ [Client Configuration] @@ -135,14 +138,15 @@ │ [System Info] │ Redacted To Not Change The Baselines From Run To Run │ ) - └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds - └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds - ( - [Client Side Request Stats] - Redacted To Not Change The Baselines From Run To Run - ) + └── Microsoft.Azure.Cosmos.Handlers.TelemetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Cosmos.Handlers.RetryHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Cosmos.Handlers.RouterHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Cosmos.Handlers.TransportHandler(00000000-0000-0000-0000-000000000000) RequestHandler-Component 00:00:00:000 0.00 milliseconds + └── Microsoft.Azure.Documents.ServerStoreModel Transport Request(00000000-0000-0000-0000-000000000000) Transport-Component 00:00:00:000 0.00 milliseconds + ( + [Client Side Request Stats] + Redacted To Not Change The Baselines From Run To Run + ) ]]> CreateClientAndContainer(ConnectionMode mode, : this.cosmosClientBuilder.Build()); // Making sure client telemetry is enabled - Assert.IsNotNull(this.GetClient().DocumentClient.clientTelemetry); + Assert.IsNotNull(this.GetClient().DocumentClient.telemetryToServiceHelper); this.database = await this.GetClient().CreateDatabaseAsync(Guid.NewGuid().ToString()); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Contracts/BenchmarkResults.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Contracts/BenchmarkResults.json index a06dabf09c..8e45a72922 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Contracts/BenchmarkResults.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Contracts/BenchmarkResults.json @@ -39,7 +39,7 @@ "MockedItemBenchmark.ReadItemNotExists;[Type=OfT]": 43489.25, "MockedItemBenchmark.ReadItemNotExists;[Type=OfTCustom]": 43490, "MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithClientTelemetryEnabled]": 43489.25, - "MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithDiagnosticsToString]": 58054, + "MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithDiagnosticsToString]": 64764.75, "MockedItemBenchmark.ReadItemNotExists;[Type=Stream]": 39044, "MockedItemBenchmark.UpdateItem;[Type=OfT]": 36591, "MockedItemBenchmark.UpdateItem;[Type=OfTCustom]": 36594.25, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs index 2baee14bd8..3403269950 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs @@ -6,7 +6,6 @@ namespace Microsoft.Azure.Cosmos.Performance.Tests { using System; using System.Globalization; - using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos; @@ -19,11 +18,10 @@ namespace Microsoft.Azure.Cosmos.Performance.Tests using Moq; using System.Collections.ObjectModel; using System.Collections.Generic; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using System.IO; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; using Newtonsoft.Json; + using Microsoft.Azure.Cosmos.Telemetry; internal class MockDocumentClient : DocumentClient, ICosmosAuthorizationTokenProvider { @@ -47,7 +45,18 @@ public static CosmosClient CreateMockCosmosClient( bool? isClientTelemetryEnabled = null, Action < CosmosClientBuilder> customizeClientBuilder = null) { - MockDocumentClient documentClient = new MockDocumentClient(); + ConnectionPolicy policy = new ConnectionPolicy(); + + if (isClientTelemetryEnabled.HasValue) + { + policy = new ConnectionPolicy + { + EnableClientTelemetry = isClientTelemetryEnabled.Value + }; + + } + + MockDocumentClient documentClient = new MockDocumentClient(policy); CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder("http://localhost", Convert.ToBase64String(Guid.NewGuid().ToByteArray())); cosmosClientBuilder.WithConnectionModeDirect(); customizeClientBuilder?.Invoke(cosmosClientBuilder); @@ -61,11 +70,6 @@ public static CosmosClient CreateMockCosmosClient( }); } - if (isClientTelemetryEnabled.HasValue && isClientTelemetryEnabled.Value) - { - cosmosClientBuilder.WithTelemetryEnabled(); - } - documentClient.dummyHeaderNames = new string[100]; for (int i = 0; i < documentClient.dummyHeaderNames.Length; i++) { @@ -79,8 +83,8 @@ public static CosmosClient CreateMockCosmosClient( return cosmosClientBuilder.Build(documentClient); } - public MockDocumentClient() - : base(new Uri("http://localhost"), null) + public MockDocumentClient(ConnectionPolicy policy = null) + : base(new Uri("http://localhost"), connectionPolicy: policy) { this.authKeyHashFunction = new StringHMACSHA256Hash(MockDocumentClient.GenerateRandomKey()); @@ -212,6 +216,13 @@ private void Init() this.globalEndpointManager = new Mock(this, new ConnectionPolicy()); + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob("perf-test-client", + this.ConnectionPolicy, + new Mock().Object, + new Mock().Object, + this.ServiceEndpoint, + this.GlobalEndpointManager, + default); this.InitStoreModels(); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs index ae5dccd2c8..d3d0e45653 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/HandlerTests.cs @@ -36,6 +36,7 @@ public void HandlerOrder() { typeof(RequestInvokerHandler), typeof(DiagnosticsHandler), + typeof(TelemetryHandler), typeof(RetryHandler), typeof(RouterHandler) }; @@ -50,29 +51,6 @@ public void HandlerOrder() Assert.IsNull(handler); } - [TestMethod] - public void HandlerOrderIfTelemetryIsEnabled() - { - using CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(enableTelemetry: true); - - Type[] types = new Type[] - { - typeof(RequestInvokerHandler), - typeof(DiagnosticsHandler), - typeof(TelemetryHandler), - typeof(RetryHandler), - typeof(RouterHandler) - }; - - RequestHandler handler = client.RequestHandler; - foreach (Type type in types) - { - Assert.IsTrue(type.Equals(handler.GetType()), $"{type} is not equal to {handler.GetType()}"); - handler = handler.InnerHandler; - } - Assert.IsNull(handler); - } - [TestMethod] public async Task TestPreProcessingHandler() { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockCosmosUtil.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockCosmosUtil.cs index 8ea04f99a5..92a6e60038 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockCosmosUtil.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockCosmosUtil.cs @@ -34,16 +34,15 @@ public static CosmosClient CreateMockCosmosClient( Cosmos.ConsistencyLevel? accountConsistencyLevel = null, bool enableTelemetry = false) { - DocumentClient documentClient = accountConsistencyLevel.HasValue ? new MockDocumentClient(accountConsistencyLevel.Value) : new MockDocumentClient(); - CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder("http://localhost", MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey); - customizeClientBuilder?.Invoke(cosmosClientBuilder); - if(enableTelemetry) + ConnectionPolicy policy = new ConnectionPolicy { - documentClient.clientTelemetry = new Mock().Object; - - cosmosClientBuilder.WithTelemetryEnabled(); - } + EnableClientTelemetry = enableTelemetry + }; + DocumentClient documentClient = accountConsistencyLevel.HasValue ? new MockDocumentClient(accountConsistencyLevel.Value, policy) : new MockDocumentClient(policy); + CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder("http://localhost", MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey); + + customizeClientBuilder?.Invoke(cosmosClientBuilder); return cosmosClientBuilder.Build(documentClient); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs index 12d776fb38..b0c29eb016 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Utils/MockDocumentClient.cs @@ -15,6 +15,7 @@ namespace Microsoft.Azure.Cosmos.Tests using Microsoft.Azure.Cosmos.Common; using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; using Microsoft.Azure.Cosmos.Routing; + using Microsoft.Azure.Cosmos.Telemetry; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Collections; @@ -27,14 +28,14 @@ internal class MockDocumentClient : DocumentClient, IAuthorizationTokenProvider, Mock partitionKeyRangeCache; private readonly Cosmos.ConsistencyLevel accountConsistencyLevel; - public MockDocumentClient() - : base(new Uri("http://localhost"), MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey) + public MockDocumentClient(ConnectionPolicy connectionPolicy = null) + : base(new Uri("http://localhost"), MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey, connectionPolicy) { this.Init(); } - public MockDocumentClient(Cosmos.ConsistencyLevel accountConsistencyLevel) - : base(new Uri("http://localhost"), null) + public MockDocumentClient(Cosmos.ConsistencyLevel accountConsistencyLevel, ConnectionPolicy connectionPolicy = null) + : base(new Uri("http://localhost"), connectionPolicy) { this.accountConsistencyLevel = accountConsistencyLevel; this.Init(); @@ -261,6 +262,14 @@ private void Init() this.MockGlobalEndpointManager.Setup(gep => gep.ResolveServiceEndpoint(It.IsAny())).Returns(new Uri("http://localhost")); this.MockGlobalEndpointManager.Setup(gep => gep.InitializeAccountPropertiesAndStartBackgroundRefresh(It.IsAny())); SessionContainer sessionContainer = new SessionContainer(this.ServiceEndpoint.Host); + + this.telemetryToServiceHelper = TelemetryToServiceHelper.CreateAndInitializeClientConfigAndTelemetryJob("test-client", + this.ConnectionPolicy, + new Mock().Object, + new Mock().Object, + this.ServiceEndpoint, + this.GlobalEndpointManager, + default); this.sessionContainer = sessionContainer; } From f56944ef55fef63549067823d6274d867a44c065 Mon Sep 17 00:00:00 2001 From: Matias Quaranta Date: Wed, 23 Aug 2023 08:30:36 -0700 Subject: [PATCH 17/20] [Internal] Automation: Adds logic to tag customer-reported issues (#4047) * Added customer-reported label * Changing condition * padding * more padding * permission name * padding --- .github/policies/resourceManagement.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index 8207a0708a..7d78118b0e 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -38,6 +38,20 @@ configuration: - addLabel: label: needs-investigation description: + - if: + - payloadType: Issues + - isAction: + action: Opened + - not: + or: + - activitySenderHasPermission: + permission: Write + - activitySenderHasPermission: + permission: Admin + then: + - addLabel: + label: customer-reported + description: Identifies issues created by users without write access as customers - if: - payloadType: Pull_Request - hasLabel: From 83dd4b54348a4b2d172d1bbcfd830134363e212b Mon Sep 17 00:00:00 2001 From: David Chaava Date: Wed, 23 Aug 2023 21:57:27 +0200 Subject: [PATCH 18/20] [Internal] Benchmark tool: Adds requests diagnostic data capture and upload to storage (#3926) * azure-cosmos-dotnet-v3/issues/3889 add diagnostics data capturing during bechmark and storing into blob storage after finish * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh * fix bug * fix review comments * fix comments * fix comments * fix case * add tests and refactoring * fix * unify logging * add summaries * fix method summary * fix BOM * fix review comments * fix comment * fix line breaks * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/README.md * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md * catch exceptions * add container prefix * ResultStorageContainerPrefix * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * Update Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs Co-authored-by: Kiran Kumar Kolli * Update Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json Co-authored-by: Matias Quaranta * Update Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs Co-authored-by: Kiran Kumar Kolli * Update Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/DiagnosticDataListener.cs Co-authored-by: Matias Quaranta * fix comments * fix comments * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh * make BlobCLient Lazy singleton * new file: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh * check on diagnostic colletiong * remove locks and improve logs appending * removed unnecesary directory * removed unnecesary directory * removed unnecesary directory * new file: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/README.md * add dashboard * fix arm template * change branch * fix * add dashboard name * fix dashboard * add logging * fix * trace error * fix devide zero * add trace errors * fix * fix * fix * fix * fix * migrate to text writer * fixes * diagnostic logs * add diagnostic logs * remove flush and reset * metric collection window lock * collection window * force flush every n seconds * fix bug * fix * Update Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md Co-authored-by: Matias Quaranta * change deafult metric interval * constant * fix container creating conflict issue * change azuredeply branch name * remove ArmTemplate folder * fix DiagnosticLatencyThresholdInMs default value --------- Co-authored-by: David Chaava Co-authored-by: Kiran Kumar Kolli Co-authored-by: Matias Quaranta --- .../Tools/Benchmark/ARMTemplate/README.md | 73 -- .../Tools/Benchmark/ARMTemplate/arm1.png | Bin 73958 -> 0 bytes .../Tools/Benchmark/ARMTemplate/arm2.png | Bin 85941 -> 0 bytes .../Tools/Benchmark/ARMTemplate/arm3.png | Bin 15913 -> 0 bytes .../ARMTemplate/benchmarkTemplate.json | 154 --- .../Benchmark/ARMTemplate/parameters.json | 39 - .../Tools/Benchmark/ARMTemplate/run.sh | 2 - .../Benchmark/AzureVmBenchmark/README.md | 111 ++ .../AzureVmBenchmark/azuredeploy.json | 1160 +++++++++++++++++ .../AzureVmBenchmark/scripts/custom-script.sh | 28 + .../AzureVmBenchmark/scripts/execute.sh | 9 + .../AzureVmBenchmark/system/cloud-init.txt | 16 + .../Tools/Benchmark/BenchmarkConfig.cs | 10 + .../Benchmark/BenchmarkLatencyEventSource.cs | 5 +- .../Tools/Benchmark/BenchmarkProgress.cs | 50 + .../Tools/Benchmark/CosmosBenchmark.csproj | 1 + .../Benchmark/Fx/DiagnosticDataListener.cs | 224 ++++ .../Tools/Benchmark/Fx/MetricsCollector.cs | 16 +- .../Benchmark/Fx/MetricsCollectorProvider.cs | 50 +- .../Benchmark/Fx/ParallelExecutionStrategy.cs | 9 +- .../Benchmark/Fx/SerialOperationExecutor.cs | 6 +- .../Tools/Benchmark/Fx/TelemetrySpan.cs | 8 +- .../Tools/Benchmark/Program.cs | 111 +- .../Tools/Benchmark/README.md | 5 + .../Tools/Benchmark/Utility.cs | 6 + .../CosmosBenchmarkTests.csproj | 23 + .../Fx/DiagnosticDataListenerTests.cs | 54 + 27 files changed, 1845 insertions(+), 325 deletions(-) delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/README.md delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/arm1.png delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/arm2.png delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/arm3.png delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/benchmarkTemplate.json delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/parameters.json delete mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/run.sh create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/system/cloud-init.txt create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkProgress.cs create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/DiagnosticDataListener.cs create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/CosmosBenchmarkTests.csproj create mode 100644 Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/Fx/DiagnosticDataListenerTests.cs diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/README.md b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/README.md deleted file mode 100644 index 7821e1eefd..0000000000 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Running benchmarks on ARM Tempaltes - -[ARM Templates](https://learn.microsoft.com/azure/azure-resource-manager/templates/) makes executing the Azure Cosmos DB SDK Benchmark extremely easy, with very few steps involved. Plus, it lets you test and evaluate performance quickly on multiple resource (CPU/RAM) configurations and across multiple Azure regions seamlessly. - -For the below steps, you will **need an Azure Subscription**. - -## Steps - -### Deploy with one click - -Just click in the **Deploy to Azure button** and it will guide you into automatically configuring, deploying, and running the benchmark. - -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-cosmos-dotnet-v3%2Fmaster%2FMicrosoft.Azure.Cosmos.Samples%2FTools%2FBenchmark%2FARMTemplate%2FbenchmarkTemplate.json) - -Please populate the `endpoint` and `key` for your Azure Cosmos DB account. You can [obtain these from the Azure Portal or through CLI](https://learn.microsoft.com/azure/cosmos-db/secure-access-to-data?tabs=using-primary-key#primary-keys). - -Optionally you can modify the other parameters, such as the `throughput` for the container that will get created, the amount of `documents` to insert, the degree of `parallelism`, and if you want the container to be deleted after the benchmark is run (`cleanUpOnFinish` `true/false`). - -Additionally, the template lets you customize the size of the container instance that will be deployed, which you can make as similar as possible to the instance you will be running in production to simulate results. - -### Deploy with Azure CLI - -First open the `parameters.json` file and populate the `endpoint` and `key` for your Azure Cosmos DB account. You can [obtain these from the Azure Portal or through CLI](https://learn.microsoft.com/azure/cosmos-db/secure-access-to-data?tabs=using-primary-key#primary-keys). - - Next, modify the other parameters, such as the `throughput` for the container that will get created, the amount of `documents` to insert, the degree of `parallelism`, and if you want the container to be deleted after the benchmark is run (`cleanUpOnFinish` `true/false`). - - If you're deploying the template to a resource group that does not exist you must create one first. Please note that the name of the resource group can only include alphanumeric characters, periods, underscores, hyphens, and parenthesis. It can be up to 90 characters. The name can't end in a period. To create a resource group use the following command: - ```bash - az group create --name $resourceGroupName --location $location - ``` - - To run the benchmark first navigate to the directory the `benchmarkTemplate.json` and `parameters.json` files are stored and use the Azure CLI with the following command: - ```bash - az deployment group create --resource-group $resourceGroupName --template-file benchmarkTemplate.json --parameters @parameters.json - ``` - -### The Benchmark - -Once you create the benchmark, it will do the following: - -1. It will create a Linux container named `cosmosdbsdkperf` and provision an image with NET 6 inside an instance with the configured CPU and RAM. -![Provisioned Container Instance](./arm1.png) -2. Clone the Azure Cosmos DB SDK repository with the required files -3. Execute the benchmark and provide output logs -4. Stop the instance - -While the container instance is running (or after), you can either use the Azure Portal or the Azure CLI to check the benchmark results with: - -```bash -az container logs -g $resourceGroupName -n cosmosdbsdkperf -``` - -Additionally you can check the logs in the Azure Portal by navigating to the container instance and clicking on the **Logs** tab. - -The logs will show the information, including the initial parameters: - -![Initial benchmark parameters](./arm2.png) - -And the results: - -![Benchmark results](./arm3.png) - -### Clean up - -If you want to remove the Benchmark instance, ypu can delete the container from the Azure Portal. You can also do so from the Azure CLI: - -```bash -az container delete -g $resourceGroupName -n cosmosdbsdkperf -``` - -You can also delete the Benchmark instance from the Azure Portal by navigating to the container instance and clicking on the **Delete** button. - -**Remember to delete the Database and Container** that were created for the Benchmark in your CosmosDB account if you did not use the `CLEANUPFINISH` parameter as `true`. diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/arm1.png b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/arm1.png deleted file mode 100644 index c740b8b3d7528c4f7232b24da55fee45cdb4f6c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73958 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%0D|IlC+?d0-n`+H&eX$k1t(WTw;An|l8=kkjqf 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 ADF6Tf diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/benchmarkTemplate.json b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/benchmarkTemplate.json deleted file mode 100644 index 13bb3070da..0000000000 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/benchmarkTemplate.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "endpoint": { - "type": "string", - "metadata": { - "description": "Your account endpoint" - } - }, - "key": { - "type": "securestring", - "metadata": { - "description": "Your account key" - } - }, - "throughput": { - "type": "string", - "metadata": { - "description": "The throughput for the container." - } - }, - "documents": { - "type": "string", - "metadata": { - "description": "The amount of items to insert." - } - }, - "parallelism": { - "type": "string", - "metadata": { - "description": "The degree of parallelism." - }, - "defaultValue": "-1" - }, - "cleanUpOnFinish": { - "type": "string", - "metadata": { - "description": "Whether to delete the container after the benchmark is run." - }, - "defaultValue": "false" - }, - "containerGroupName": { - "type": "string", - "metadata": { - "description": "Name for the container group" - }, - "defaultValue": "CosmosDBBenchmark" - }, - "containerName": { - "type": "string", - "metadata": { - "description": "Name for the container" - }, - "defaultValue": "cosmosdbsdkperf" - }, - "volumeName": { - "type": "string", - "metadata": { - "description": "Name for the gitRepo volume" - }, - "defaultValue": "cosmosdbsdk" - }, - "port": { - "type": "string", - "metadata": { - "description": "Port to open on the container and the public IP address." - }, - "defaultValue": "80" - }, - "cpuCores": { - "type": "string", - "metadata": { - "description": "The number of CPU cores to allocate to the container." - }, - "defaultValue": "4" - }, - "memoryInGb": { - "type": "string", - "metadata": { - "description": "The amount of memory to allocate to the container in gigabytes." - }, - "defaultValue": "8" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Location for all resources." - } - } - }, - "variables": {}, - "resources": [ - { - "name": "[parameters('containerGroupName')]", - "type": "Microsoft.ContainerInstance/containerGroups", - "apiVersion": "2020-11-01", - "location": "[parameters('location')]", - "properties": { - "containers": [ - { - "name": "[parameters('containerName')]", - "properties": { - "image": "mcr.microsoft.com/dotnet/sdk:6.0", - "ports": [ - { - "port": "[parameters('port')]" - } - ], - "resources": { - "requests": { - "cpu": "[parameters('cpuCores')]", - "memoryInGB": "[parameters('memoryInGb')]" - } - }, - "volumeMounts": [ - { - "name": "[parameters('volumeName')]", - "mountPath": "/mnt/gitrepos/", - "readOnly": false - } - ], - "command": [ - "/bin/bash", - "-c", - "[concat('cd /mnt/gitrepos/azure-cosmos-dotnet-v3/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate;export ENDPOINT=\"', parameters('endpoint'),'\";export KEY=\"', parameters('key'),'\";export THROUGHPUT=\"', parameters('throughput'),'\";export DOCUMENTS=\"', parameters('documents'),'\";export PARALLELISM=\"', parameters('parallelism'),'\";export CLEANUPFINISH=\"', parameters('cleanUpOnFinish'),'\";chmod +x run.sh;./run.sh')]" - ] - } - } - ], - "osType": "Linux", - "restartPolicy": "Never", - "ipAddress": { - "type": "Public", - "ports": [ - { - "protocol": "TCP", - "port": "[parameters('port')]" - } - ] - }, - "volumes": [ - { - "name": "[parameters('volumeName')]", - "gitRepo": { - "repository": "https://github.com/Azure/azure-cosmos-dotnet-v3" - } - } - ] - } - } - ] -} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/parameters.json b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/parameters.json deleted file mode 100644 index f8b1983b33..0000000000 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/parameters.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "endpoint": { - "value": "" - }, - "key": { - "value": "" - }, - "throughput": { - "value": "" - }, - "documents": { - "value": "" - }, - "parallelism": { - "value": "-1" - }, - "cleanUpOnFinish": { - "value": "false" - }, - "containergroupname": { - "value": "CosmosDBBenchmark" - }, - "containername": { - "value": "cosmosdbsdkperf" - }, - "volumename": { - "value": "cosmosdbsdk" - }, - "cpuCores": { - "value": "4" - }, - "memoryInGb": { - "value": "8" - } - } -} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/run.sh b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/run.sh deleted file mode 100644 index 3b8855a95f..0000000000 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/ARMTemplate/run.sh +++ /dev/null @@ -1,2 +0,0 @@ -cd .. -dotnet run -c Release -e ${ENDPOINT} -k ${KEY} -t ${THROUGHPUT} -n ${DOCUMENTS} --pl ${PARALLELISM} --CleanupOnFinish ${CLEANUPFINISH} -w InsertV2BenchmarkOperation \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md new file mode 100644 index 0000000000..cd9ad834fc --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md @@ -0,0 +1,111 @@ +# Running benchmarks on Azure Virtual Machines + +[Azure Arm Template](https://azure.microsoft.com/products/arm-templates/) makes executing the Azure Cosmos DB SDK Benchmark extremely easy. And reaching following abilities: + +- Ability to execute benchmarking on multiple machines with one ARM Template +- Capturing diagnostics data for requests that exceed the latency threshold +- AppInsights: Live metrics (10s) + - Success rate + - Error + - P90, P99, P999, P9999 + +| Parameter name | Description | +| ------------------------------------------------------ | ------------------------------------------------------ | +| projectName | Specifies a name for generating resource names. | +| location | Specifies the location for all resources. | +| adminUsername | Specifies a username for the Virtual Machine. | +| adminPassword | Specifies a password for the Virtual Machine. | +| vmSize | Specifies a Virtual Machine size | +| vNetName | Specifies a Virtual Network name | +| vNetAddressPrefixes | Specifies a Virtual Network Address Prefix | +| vNetSubnetName | Specifies a Virtual Network Subnet name | +| vNetSubnetAddressPrefix | Specifies a Virtual Network Subnet Address Prefix | +| cosmosURI | Specifies the URI of the Cosmos DB account | +| cosmosKey | Specifies the key for the Cosmos DB account | +| throughput | Specifies Collection throughput use | +| operationsCount | Specifies the number of operations to execute | +| parallelism | Specifies the degree of parallelism | +| resultsContainer | Specifies the name of the container to which the results to be saved | +| vmCount | Specifies the number of Virtual Machines that will part of the test bench | +| workloadType | Specifies the workload | +| benchmarkingToolsBranchName | Specifies the GitHub branch for the benchmark tool source code repository | +| diagnosticsLatencyThresholdInMS | Specifies request latency threshold for capturing diagnostic data | +| storageAccountName | Specifies Storage Account Nmae | +| metricsReportingIntervalInSec | Specifies metrics reporting interval in seconds | +| applicationInsightsName | Specifies Application Insights Account Name | +| startDate | Specifies Diagnostic Blob storage container folder prefix | + + [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-cosmos-dotnet-v3%2Fmaster%2FMicrosoft.Azure.Cosmos.Samples%2FTools%2FBenchmark%2FAzureVmBenchmark%2Fazuredeploy.json) + +After benchmark was launched use Shared Dashboard for viewing charts with + +## Workload types + +| Workload Type | Description | +| ------------------------------------------------------ | ------------------------------------------------------ | +| InsertV3BenchmarkOperation | Inserts single document | +| QueryStreamCrossPkDistinctFullDrainV3BenchmarkOperation | Execute a `DISTINCT` query cross partitions and access data using a steram | +| QueryStreamCrossPkDistinctWithPaginationV3BenchmarkOperation | Execute a `DISTINCT` query cross partitions and access data using a steram and pagination | +| QueryStreamCrossPkGroupByFullDrainV3BenchmarkOperation | Execute a `GROUP BY` query cross partitions and access data using a steram | +| QueryStreamCrossPkGroupByWithPaginationV3BenchmarkOperation | Execute a `GROUP BY` query cross partitions and access data using a steram and pagination | +| QueryStreamCrossPkOrderByFullDrainV3BenchmarkOperation | Execute a `ORDER BY` query cross partitions and access data using a steram | +| QueryStreamCrossPkOrderByWithPaginationV3BenchmarkOperation | Execute a `ORDER BY` query cross partitions and access data using a steram and pagination | +| QueryStreamCrossPkV3BenchmarkOperation | Execute `select * from T where T.id = @id` query cross partitions and access data using a steram | +| QueryStreamCrossPkWithPaginationV3BenchmarkOperation | Execute `select * from T where T.id = @id` query cross partitions and access data using a steram and pagination | +| QueryStreamSinglePkDistinctFullDrainV3BenchmarkOperation | Execute a `DISTINCT` query on a single partition and access data using a steram | +| QueryStreamSinglePkDistinctWithPaginationV3BenchmarkOperation | Execute a `DISTINCT` query on a single partition and access data using a steram and pagination | +| QueryStreamSinglePkGroupByFullDrainV3BenchmarkOperation | Execute a `GROUP BY` query on a single partition and access data using a steram | +| QueryStreamSinglePkGroupByWithPaginationV3BenchmarkOperation | Execute a `GROUP BY` query on a single partition and access data using a steram and pagination | +| QueryStreamSinglePkOrderByFullDrainV3BenchmarkOperation | Execute a `ORDER BY` query on a single partition and access data using a stream | +| QueryStreamSinglePkOrderByWithPaginationV3BenchmarkOperation | Execute a `ORDER BY` query on a single partition and access data using a steram and pagination | +| QueryStreamSinglePkV3BenchmarkOperation | Execute `select * from T where T.id = @id` query on a single partition and access data using a steram | +| QueryStreamSinglePkWithPaginationV3BenchmarkOperation | Execute `select * from T where T.id = @id` query on a single partition and access data using a steram and pagination | +| QueryTCrossPkDistinctFullDrainV3BenchmarkOperation | Execute a `DISTINCT` query cross partitions and access data | +| QueryTCrossPkDistinctWithPaginationV3BenchmarkOperation | Execute a `DISTINCT` query cross partitions and access data using a pagination | +| QueryTCrossPkGroupByFullDrainV3BenchmarkOperation | Execute a `GROUP BY` query cross partitions and access data | +| QueryTCrossPkGroupByWithPaginationV3BenchmarkOperation | Execute a `GROUP BY` query cross partitions and access data using a pagination | +| QueryTCrossPkOrderByFullDrainV3BenchmarkOperation | Execute a `ORDER BY` query cross partitions and access data | +| QueryTCrossPkOrderByWithPaginationV3BenchmarkOperation | Execute a `ORDER BY` query cross partitions and access data using a pagination | +| QueryTCrossPkV3BenchmarkOperation | Execute `select * from T where T.id = @id` query cross partitions partition and access data | +| QueryTCrossPkWithPaginationV3BenchmarkOperation | Execute `select * from T where T.id = @id` query cross partitions partition and access data using a pagination | +| QueryTSinglePkDistinctFullDrainV3BenchmarkOperation | Execute a `DISTINCT` query on a single partition and access data | +| QueryTSinglePkDistinctWithPaginationV3BenchmarkOperation | Execute a `DISTINCT` query on a single partition and access data using a pagination | +| QueryTSinglePkGroupByFullDrainV3BenchmarkOperation | Execute a `GROUP BY` query on a single partition and access data | +| QueryTSinglePkGroupByWithPaginationV3BenchmarkOperation | Execute a `GROUP BY` query on a single partition and access data using a pagination | +| QueryTSinglePkOrderByFullDrainV3BenchmarkOperation | Execute a `ORDER BY` query on a single partition and access data | +| QueryTSinglePkOrderByWithPaginationV3BenchmarkOperation | Execute a `ORDER BY` query on a single partition and access data using a pagination | +| QueryTSinglePkV3BenchmarkOperation | Execute `select * from T where T.id = @id` query on a single partition and access data | +| QueryTSinglePkWithPaginationV3BenchmarkOperation | Execute `select * from T where T.id = @id` query on a single partition and access data using a pagination | +| ReadFeedStreamV3BenchmarkOperation | Execute Read Feed query | +| ReadNotExistsV3BenchmarkOperation | Execute query for not existing item | +| ReadStreamExistsV3BenchmarkOperation | Read item stream | +| ReadStreamExistsWithDiagnosticsV3BenchmarkOperation | Read item stream with diagnostics data | +| ReadTExistsV3BenchmarkOperation | Read single item | + + +## Diagnose and troubleshooting +The Benchmark tool output logs may be found on the each Virtual Machine in user home directory. + +- Connect to VM using serial console +- Enter login and password + +```bash +benchmarking@Benchmarking-dotnet-SDK-vm1:~$ ls + +agent.err agent.out +``` + +```bash +benchmarking@Benchmarking-dotnet-SDK-vm1:~$ cat agent.out + +CSC : warning CS8002: Referenced assembly 'MathNet.Numerics, Version=4.15.0.0, Culture=neutral, PublicKeyToken=null' does not have a strong name. [/var/lib/waagent/custom-script/download/2/azure-cosmos-dotnet-v3/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj] +Azure VM Location:eastus +BenchmarkConfig arguments +IsServerGC: True +--------------------------------------------------------------------- +{ + "WorkloadType": "ReadTExistsV3BenchmarkOperation", + "EndPoint": "https://cosmosdb-benchmark-dotnet-test-thr.documents.azure.com:443/", + "Database": "db", + "Container": "data", +``` diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json new file mode 100644 index 0000000000..347857cf82 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json @@ -0,0 +1,1160 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "projectName": { + "type": "string", + "defaultValue": "Benchmarking-dotnet-SDK", + "metadata": { + "description": "Specifies a name for generating resource names." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location for all resources." + } + }, + "adminUsername": { + "type": "string", + "defaultValue": "benchmarking", + "metadata": { + "description": "Specifies a username for the Virtual Machine." + } + }, + "adminPassword": { + "type": "securestring", + "metadata": { + "description": "Specifies a password for the Virtual Machine." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D4s_v3", + "metadata": { + "description": "Specifies a Virtual Machine size" + } + }, + "vNetName": { + "type": "string", + "defaultValue": "Benchmarking-dotnet-SDK-vnet", + "metadata": { + "description": "Specifies a Virtual Network name" + } + }, + "vNetAddressPrefixes": { + "type": "string", + "defaultValue": "10.2.0.0/16", + "metadata": { + "description": "Specifies a Virtual Network Address Prefix" + } + }, + "vNetSubnetName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Specifies a Virtual Network Subnet name" + } + }, + "vNetSubnetAddressPrefix": { + "type": "string", + "defaultValue": "10.2.0.0/24", + "metadata": { + "description": "Specifies a Virtual Network Subnet Address Prefix " + } + }, + "cosmosURI": { + "type": "string", + "metadata": { + "description": "Specifies the URI of the Cosmos DB account" + } + }, + "cosmosKey": { + "type": "securestring", + "metadata": { + "description": "Specifies the key for the Cosmos DB account" + } + }, + "throughput": { + "type": "int", + "defaultValue": 100000, + "metadata": { + "description": "Specifies Collection throughput use" + } + }, + "operationsCount": { + "type": "int", + "metadata": { + "description": "Specifies the number of operations to execute" + } + }, + "parallelism": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Specifies the degree of parallelism" + } + }, + "resultsContainer": { + "type": "string", + "defaultValue": "BenchmarkResult", + "metadata": { + "description": "Specifies the name of the container to which the results to be saved" + } + }, + "vmCount": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "Specifies the number of Virtual Machines that will part of the test bench" + } + }, + "workloadType": { + "type": "string", + "allowedValues": [ + "InsertV3BenchmarkOperation", + "QueryStreamCrossPkDistinctFullDrainV3BenchmarkOperation", + "QueryStreamCrossPkDistinctWithPaginationV3BenchmarkOperation", + "QueryStreamCrossPkGroupByFullDrainV3BenchmarkOperation", + "QueryStreamCrossPkGroupByWithPaginationV3BenchmarkOperation", + "QueryStreamCrossPkOrderByFullDrainV3BenchmarkOperation", + "QueryStreamCrossPkOrderByWithPaginationV3BenchmarkOperation", + "QueryStreamCrossPkV3BenchmarkOperation", + "QueryStreamCrossPkWithPaginationV3BenchmarkOperation", + "QueryStreamSinglePkDistinctFullDrainV3BenchmarkOperation", + "QueryStreamSinglePkDistinctWithPaginationV3BenchmarkOperation", + "QueryStreamSinglePkGroupByFullDrainV3BenchmarkOperation", + "QueryStreamSinglePkGroupByWithPaginationV3BenchmarkOperation", + "QueryStreamSinglePkGroupByWithPaginationV3BenchmarkOperation", + "QueryStreamSinglePkOrderByWithPaginationV3BenchmarkOperation", + "QueryStreamSinglePkV3BenchmarkOperation", + "QueryStreamSinglePkWithPaginationV3BenchmarkOperation", + "QueryTCrossPkDistinctFullDrainV3BenchmarkOperation", + "QueryTCrossPkDistinctWithPaginationV3BenchmarkOperation", + "QueryTCrossPkGroupByFullDrainV3BenchmarkOperation", + "QueryTCrossPkGroupByWithPaginationV3BenchmarkOperation", + "QueryTCrossPkOrderByFullDrainV3BenchmarkOperation", + "QueryTCrossPkOrderByWithPaginationV3BenchmarkOperation", + "QueryTCrossPkV3BenchmarkOperation", + "QueryTCrossPkWithPaginationV3BenchmarkOperation", + "QueryTSinglePkDistinctFullDrainV3BenchmarkOperation", + "QueryTSinglePkDistinctWithPaginationV3BenchmarkOperation", + "QueryTSinglePkGroupByFullDrainV3BenchmarkOperation", + "QueryTSinglePkGroupByWithPaginationV3BenchmarkOperation", + "QueryTSinglePkOrderByFullDrainV3BenchmarkOperation", + "QueryTSinglePkOrderByWithPaginationV3BenchmarkOperation", + "QueryTSinglePkV3BenchmarkOperation", + "QueryTSinglePkWithPaginationV3BenchmarkOperation", + "ReadFeedStreamV3BenchmarkOperation", + "ReadNotExistsV3BenchmarkOperation", + "ReadStreamExistsV3BenchmarkOperation", + "ReadStreamExistsWithDiagnosticsV3BenchmarkOperation", + "ReadTExistsV3BenchmarkOperation" + ], + "defaultValue": "ReadTExistsV3BenchmarkOperation", + "metadata": { + "description": "Specifies the workload" + } + }, + "benchmarkingToolsBranchName": { + "type": "string", + "defaultValue": "master", + "metadata": { + "description": "Specifies the GitHub branch for the benchmark tool source code repository" + } + }, + "diagnosticsLatencyThresholdInMS": { + "type": "int", + "defaultValue": 100 + }, + "storageAccountName": { + "type": "string", + "defaultValue": "dotnetbenchmark", + "metadata": { + "description": "Specifies Storage Account Nmae" + } + }, + "metricsReportingIntervalInSec": { + "type": "int", + "defaultValue": 5 + }, + "applicationInsightsName": { + "type": "string", + "defaultValue": "CosmosDBBenchmarkTestAppInsights", + "metadata": { + "description": "Specifies Application Insights Account Name" + } + }, + "startDate": { + "type": "int", + "defaultValue": "[dateTimeToEpoch(utcNow())]" + }, + "dashboardName": { + "type": "string", + "defaultValue": "Cosmos Benchmark Statistics", + "metadata": { + "description": "Specifies Application Insights Dashboard Name" + } + } + }, + "variables": { + "vmName": "[concat(parameters('projectName'), '-vm')]", + "publicIPAddressName": "[concat(parameters('projectName'), '-ip')]", + "networkInterfaceName": "[concat(parameters('projectName'), '-nic')]", + "networkSecurityGroupName": "[concat(parameters('projectName'), '-nsg')]", + "benchmarkingToolsURL": "https://github.com/Azure/azure-cosmos-dotnet-v3.git", + "cloudInitScriptUrl": "[concat('https://raw.githubusercontent.com/Azure/azure-cosmos-dotnet-v3/',parameters('benchmarkingToolsBranchName'),'/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/system/cloud-init.txt')]", + "vmScriptExtensionScriptUrl": "[concat('https://raw.githubusercontent.com/Azure/azure-cosmos-dotnet-v3/',parameters('benchmarkingToolsBranchName'),'/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh')]", + "customScriptUrl": "[concat('https://raw.githubusercontent.com/Azure/azure-cosmos-dotnet-v3/',parameters('benchmarkingToolsBranchName'),'/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh')]", + "vmScriptExtensionScriptName": "execute.sh", + "convertedDatetime": "[dateTimeFromEpoch(parameters('startDate'))]", + "appInsightsResourceIds": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/microsoft.insights/components/', parameters('applicationInsightsName'))]", + "chart0Expression": "[concat('customMetrics\n| where name == \"ReadOperationLatencyInMs\" and timestamp > ago(1d)\n| summarize\n percentile(value, 50),\n percentile(value, 75),\n percentile(value, 90),\n percentile(value, 95)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]", + "chart1Expression": "[concat('customMetrics\n| where name == \"ReadOperationLatencyInMs\" and timestamp > ago(1d)\n| summarize\n percentile(value, 99),\n percentile(value, 99.9),\n percentile(value, 99.99)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]", + "chart2Expression": "[concat('customMetrics\n| where name == \"ReadOperationRps\" and timestamp > ago(1d)\n| summarize\n avg(value)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]" + + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "name": "[parameters('storageAccountName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "StorageV2", + "properties": { + "accessTier": "Hot" + } + }, + { + "name": "[parameters('applicationInsightsName')]", + "type": "Microsoft.Insights/components", + "apiVersion": "2015-05-01", + "location": "[resourceGroup().location]", + "properties": { + "ApplicationId": "[parameters('applicationInsightsName')]", + "Application_Type": "web" + } + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2020-11-01", + "name": "[variables('networkSecurityGroupName')]", + "location": "[parameters('location')]", + "properties": { + "securityRules": [] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2020-11-01", + "name": "[concat(variables('publicIPAddressName'),copyIndex(1))]", + "location": "[parameters('location')]", + "sku": { + "name": "Basic", + "tier": "Regional" + }, + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Dynamic", + "idleTimeoutInMinutes": 4 + }, + "copy": { + "name": "publicIPcopy", + "count": "[parameters('vmCount')]" + } + }, + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2020-11-01", + "name": "[parameters('vNetName')]", + "location": "[parameters('location')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('vNetAddressPrefixes')]" + ] + }, + "subnets": [ + { + "name": "[parameters('vNetSubnetName')]", + "properties": { + "addressPrefix": "[parameters('vNetSubnetAddressPrefix')]", + "privateEndpointNetworkPolicies": "Enabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + } + ], + "enableDdosProtection": false + } + }, + { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2020-11-01", + "name": "[concat(parameters('vNetName'), '/',parameters('vNetSubnetName'))]", + "dependsOn": [ + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vNetName'))]", + "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]" + ], + "properties": { + "addressPrefix": "[parameters('vNetSubnetAddressPrefix')]", + "privateEndpointNetworkPolicies": "Enabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName'))]" + } + } + }, + { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2020-11-01", + "name": "[concat(variables('networkInterfaceName'),copyIndex(1))]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', concat(variables('publicIPAddressName'),copyIndex(1)))]", + "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vNetName'), parameters('vNetSubnetName'))]" + ], + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig1", + "properties": { + "privateIPAddress": "10.0.0.4", + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', concat(variables('publicIPAddressName'),copyIndex(1)))]" + }, + "subnet": { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vNetName'), parameters('vNetSubnetName'))]" + }, + "primary": true, + "privateIPAddressVersion": "IPv4" + } + } + ], + "enableAcceleratedNetworking": true, + "enableIPForwarding": false + }, + "copy": { + "name": "networkInterfacecopy", + "count": "[parameters('vmCount')]" + } + }, + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-11-01", + "name": "[concat(variables('vmName'),copyIndex(1))]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('networkInterfaceName'),copyIndex(1)))]", + "[concat('Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]", + "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + ], + "tags": { + "azsecpack": "prod", + "platformsettings.host_environment.service.platform_optedin_for_rootcerts": "true" + }, + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "storageProfile": { + "imageReference": { + "publisher": "canonical", + "offer": "0001-com-ubuntu-server-focal", + "sku": "20_04-lts-gen2", + "version": "latest" + }, + "osDisk": { + "osType": "Linux", + "name": "[concat(variables('vmName'),copyIndex(1),'_OsDisk')]", + "createOption": "FromImage", + "caching": "ReadWrite", + "managedDisk": { + "storageAccountType": "Premium_LRS" + }, + "deleteOption": "Delete", + "diskSizeGB": 30 + } + }, + "osProfile": { + "computerName": "[concat(variables('vmName'),copyIndex(1))]", + "adminUsername": "[parameters('adminUsername')]", + "adminPassword": "[parameters('adminPassword')]", + "customData": "[base64(concat('#include\n',variables('cloudInitScriptUrl')))]", + "linuxConfiguration": { + "disablePasswordAuthentication": false, + "provisionVMAgent": true, + "patchSettings": { + "patchMode": "ImageDefault", + "assessmentMode": "ImageDefault" + } + }, + "allowExtensionOperations": true + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', concat(variables('networkInterfaceName'),copyIndex(1)))]", + "properties": { + "deleteOption": "Delete" + } + } + ] + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": true + } + } + }, + "copy": { + "name": "vmcopy", + "count": "[parameters('vmCount')]" + } + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2019-03-01", + "name": "[concat(variables('vmName'),copyIndex(1),'/CustomScript')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', concat(variables('vmName'),copyIndex(1)))]" + ], + "properties": { + "publisher": "Microsoft.Azure.Extensions", + "type": "CustomScript", + "typeHandlerVersion": "2.1", + "autoUpgradeMinorVersion": true, + "settings": { + }, + "protectedSettings": { + "commandToExecute": "[concat('ADMIN_USER_NAME=',parameters('adminUsername'),' ','BENCHMARKING_TOOLS_URL=',variables('benchmarkingToolsURL'),' ','BENCHMARKING_TOOLS_BRANCH_NAME=',parameters('benchmarkingToolsBranchName'),' ','WORKLOAD_TYPE=',parameters('workloadType'),' ','VM_NAME=',variables('vmName'),copyIndex(1), ' ','DIAGNOSTICS_STORAGE_CONNECTION_STRING=','\"',concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value),'\"',' ','COSMOS_URI=','\"',parameters('cosmosURI'),'\"', ' ', 'COSMOS_KEY=',parameters('cosmosKey'), ' ', 'THROUGHPUT=',parameters('throughput'), ' ', 'DOCUMENTS=',parameters('operationsCount'), ' ','PARALLELISM=',parameters('parallelism'), ' ','RESULTS_CONTAINER=',parameters('resultsContainer'), ' ','VM_COUNT=',parameters('vmCount'), ' ','DIAGNOSTICS_LATENCY_THRESHOLD_IN_MS=',parameters('diagnosticsLatencyThresholdInMS'),' ','DIAGNOSTICS_STORAGE_CONTAINER_PREFIX=',variables('convertedDatetime'),' ','METRICS_REPORTINT_INTERVAL_SEC=',parameters('metricsReportingIntervalInSec'), ' ','CUSTOM_SCRIPT_URL=',variables('customScriptUrl'), ' ','MACHINE_INDEX=',copyIndex(1), ' ','APP_INSIGHT_CONN_STR=','\"', reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))).ConnectionString,'\"',' ', 'bash ',variables('vmScriptExtensionScriptName'))]", + "fileUris": [ "[concat(variables('vmScriptExtensionScriptURL'))]" ] + } + }, + "copy": { + "name": "vmextensioncopy", + "count": "[parameters('vmCount')]" + } + }, + { + "name": "CosmosDBBenchmarkDashboard", + "type": "Microsoft.Portal/dashboards", + "location": "[resourceGroup().location]", + "apiVersion": "2015-08-01-preview", + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]" + ], + "tags": { + "hidden-title": "[parameters('dashboardName')]" + }, + + "properties": { + "lenses": { + "0": { + "order": 0, + "parts": { + "0": { + "position": { + "x": 0, + "y": 0, + "colSpan": 10, + "rowSpan": 6 + }, + "metadata": { + "inputs": [ + { + "name": "resourceTypeMode", + "isOptional": true + }, + { + "name": "ComponentId", + "isOptional": true + }, + { + "name": "Scope", + "value": { + "resourceIds": [ + "[variables('appInsightsResourceIds')]" + ] + }, + "isOptional": true + }, + { + "name": "PartId", + "value": "104528fc-0216-49c6-bf70-fffe9d37f93d", + "isOptional": true + }, + { + "name": "Version", + "value": "2.0", + "isOptional": true + }, + { + "name": "TimeRange", + "isOptional": true + }, + { + "name": "DashboardId", + "isOptional": true + }, + { + "name": "DraftRequestParameters", + "isOptional": true + }, + { + "name": "Query", + "value": "[variables('chart0Expression')]", + "isOptional": true + }, + { + "name": "ControlType", + "value": "FrameControlChart", + "isOptional": true + }, + { + "name": "SpecificChart", + "value": "Line", + "isOptional": true + }, + { + "name": "PartTitle", + "value": "Request latencies P50, P75, P90, P95 in ms", + "isOptional": true + }, + { + "name": "PartSubTitle", + "value": "A Latency Percentiles dashboard provides a concise overview of the distribution of response times or latencies for a system or application. It helps monitor and analyze the performance of the system, identify bottlenecks or outliers, and ensure optimal user experience.", + "isOptional": true + }, + { + "name": "Dimensions", + "value": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "percentile_value_50", + "type": "real" + }, + { + "name": "percentile_value_75", + "type": "real" + }, + { + "name": "percentile_value_90", + "type": "real" + }, + { + "name": "percentile_value_95", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "isOptional": true + }, + { + "name": "LegendOptions", + "value": { + "isEnabled": true, + "position": "Bottom" + }, + "isOptional": true + }, + { + "name": "IsQueryContainTimeRange", + "value": true, + "isOptional": true + } + ], + "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", + "settings": { + "content": { + "Dimensions": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "percentile_value_50", + "type": "real" + }, + { + "name": "percentile_value_75", + "type": "real" + }, + { + "name": "percentile_value_90", + "type": "real" + }, + { + "name": "percentile_value_95", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "IsQueryContainTimeRange": true + } + } + } + }, + "1": { + "position": { + "x": 10, + "y": 0, + "colSpan": 10, + "rowSpan": 6 + }, + "metadata": { + "inputs": [ + { + "name": "resourceTypeMode", + "isOptional": true + }, + { + "name": "ComponentId", + "isOptional": true + }, + { + "name": "Scope", + "value": { + "resourceIds": [ + "[variables('appInsightsResourceIds')]" + ] + }, + "isOptional": true + }, + { + "name": "PartId", + "value": "104528fc-0216-49c6-bf70-fffe9d37f93e", + "isOptional": true + }, + { + "name": "Version", + "value": "2.0", + "isOptional": true + }, + { + "name": "TimeRange", + "isOptional": true + }, + { + "name": "DashboardId", + "isOptional": true + }, + { + "name": "DraftRequestParameters", + "isOptional": true + }, + { + "name": "Query", + "value": "[variables('chart1Expression')]", + "isOptional": true + }, + { + "name": "ControlType", + "value": "FrameControlChart", + "isOptional": true + }, + { + "name": "SpecificChart", + "value": "Line", + "isOptional": true + }, + { + "name": "PartTitle", + "value": "Request latencies P99, P99.9, P99.99 in ms", + "isOptional": true + }, + { + "name": "PartSubTitle", + "value": "A Latency Percentiles dashboard provides a concise overview of the distribution of response times or latencies for a system or application. It helps monitor and analyze the performance of the system, identify bottlenecks or outliers, and ensure optimal user experience.", + "isOptional": true + }, + { + "name": "Dimensions", + "value": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "percentile_value_99", + "type": "real" + }, + { + "name": "percentile_value_99_9", + "type": "real" + }, + { + "name": "percentile_value_99_99", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "isOptional": true + }, + { + "name": "LegendOptions", + "value": { + "isEnabled": true, + "position": "Bottom" + }, + "isOptional": true + }, + { + "name": "IsQueryContainTimeRange", + "value": true, + "isOptional": true + } + ], + "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", + "settings": {} + } + }, + "2": { + "position": { + "x": 0, + "y": 6, + "colSpan": 5, + "rowSpan": 6 + }, + "metadata": { + "inputs": [ + { + "name": "resourceTypeMode", + "isOptional": true + }, + { + "name": "ComponentId", + "isOptional": true + }, + { + "name": "Scope", + "value": { + "resourceIds": [ + "[variables('appInsightsResourceIds')]" + ] + }, + "isOptional": true + }, + { + "name": "PartId", + "value": "104528fc-0216-49c6-bf70-fffe9d37f93e", + "isOptional": true + }, + { + "name": "Version", + "value": "2.0", + "isOptional": true + }, + { + "name": "TimeRange", + "isOptional": true + }, + { + "name": "DashboardId", + "isOptional": true + }, + { + "name": "DraftRequestParameters", + "isOptional": true + }, + { + "name": "Query", + "value": "customMetrics\n| where name == \"ReadOperationFailure\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n", + "isOptional": true + }, + { + "name": "ControlType", + "value": "FrameControlChart", + "isOptional": true + }, + { + "name": "SpecificChart", + "value": "Line", + "isOptional": true + }, + { + "name": "PartTitle", + "value": "Failed operations", + "isOptional": true + }, + { + "name": "PartSubTitle", + "value": "Provides the number of failed operations. (most likely related to some error or inavailability)", + "isOptional": true + }, + { + "name": "Dimensions", + "value": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "sum_value", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "isOptional": true + }, + { + "name": "LegendOptions", + "value": { + "isEnabled": true, + "position": "Bottom" + }, + "isOptional": true + }, + { + "name": "IsQueryContainTimeRange", + "value": true, + "isOptional": true + } + ], + "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", + "settings": { + "content": { + "Query": "customMetrics\n| where name == \"ReadOperationFailure\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n\n", + "Dimensions": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "sum_value", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + } + } + } + } + }, + "3": { + "position": { + "x": 5, + "y": 6, + "colSpan": 5, + "rowSpan": 6 + }, + "metadata": { + "inputs": [ + { + "name": "resourceTypeMode", + "isOptional": true + }, + { + "name": "ComponentId", + "isOptional": true + }, + { + "name": "Scope", + "value": { + "resourceIds": [ + "[variables('appInsightsResourceIds')]" + ] + }, + "isOptional": true + }, + { + "name": "PartId", + "value": "104528fc-0216-49c6-bf70-fffe9d37f93e", + "isOptional": true + }, + { + "name": "Version", + "value": "2.0", + "isOptional": true + }, + { + "name": "TimeRange", + "isOptional": true + }, + { + "name": "DashboardId", + "isOptional": true + }, + { + "name": "DraftRequestParameters", + "isOptional": true + }, + { + "name": "Query", + "value": "customMetrics\n| where name == \"ReadOperationSuccess\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n", + "isOptional": true + }, + { + "name": "ControlType", + "value": "FrameControlChart", + "isOptional": true + }, + { + "name": "SpecificChart", + "value": "Line", + "isOptional": true + }, + { + "name": "PartTitle", + "value": "Success operations", + "isOptional": true + }, + { + "name": "PartSubTitle", + "value": "Provides the number of successful operations.", + "isOptional": true + }, + { + "name": "Dimensions", + "value": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "sum_value", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "isOptional": true + }, + { + "name": "LegendOptions", + "value": { + "isEnabled": true, + "position": "Bottom" + }, + "isOptional": true + }, + { + "name": "IsQueryContainTimeRange", + "value": true, + "isOptional": true + } + ], + "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", + "settings": { + "content": { + "Query": "customMetrics\n| where name == \"ReadOperationSuccess\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n\n", + "Dimensions": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "sum_value", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + } + } + } + } + }, + "4": { + "position": { + "x": 10, + "y": 6, + "colSpan": 10, + "rowSpan": 6 + }, + "metadata": { + "inputs": [ + { + "name": "resourceTypeMode", + "isOptional": true + }, + { + "name": "ComponentId", + "isOptional": true + }, + { + "name": "Scope", + "value": { + "resourceIds": [ + "[variables('appInsightsResourceIds')]" + ] + }, + "isOptional": true + }, + { + "name": "PartId", + "value": "104528fc-0216-49c6-bf70-fffe9d37f93e", + "isOptional": true + }, + { + "name": "Version", + "value": "2.0", + "isOptional": true + }, + { + "name": "TimeRange", + "isOptional": true + }, + { + "name": "DashboardId", + "isOptional": true + }, + { + "name": "DraftRequestParameters", + "isOptional": true + }, + { + "name": "Query", + "value": "[variables('chart2Expression')]", + "isOptional": true + }, + { + "name": "ControlType", + "value": "FrameControlChart", + "isOptional": true + }, + { + "name": "SpecificChart", + "value": "Line", + "isOptional": true + }, + { + "name": "PartTitle", + "value": "Requests per Second (RPS)", + "isOptional": true + }, + { + "name": "PartSubTitle", + "value": "A Requests per Second (RPS) dashboard provides a concise overview of the number of requests being processed by a system or application per second.", + "isOptional": true + }, + { + "name": "Dimensions", + "value": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "RPs", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "isOptional": true + }, + { + "name": "LegendOptions", + "value": { + "isEnabled": true, + "position": "Bottom" + }, + "isOptional": true + }, + { + "name": "IsQueryContainTimeRange", + "value": true, + "isOptional": true + } + ], + "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", + "settings": { + "content": { + "Dimensions": { + "xAxis": { + "name": "ts", + "type": "datetime" + }, + "yAxis": [ + { + "name": "avg_value", + "type": "real" + } + ], + "splitBy": [], + "aggregation": "Sum" + }, + "IsQueryContainTimeRange": false + } + } + } + } + } + } + }, + "metadata": { + "model": { + "timeRange": { + "value": { + "relative": { + "duration": 24, + "timeUnit": 1 + } + }, + "type": "MsPortalFx.Composition.Configuration.ValueTypes.TimeRange" + }, + "filterLocale": { + "value": "en-us" + }, + "filters": { + "value": { + "MsPortalFx_TimeRange": { + "model": { + "format": "utc", + "granularity": "auto", + "relative": "24h" + }, + "displayCache": { + "name": "UTC Time", + "value": "Past 24 hours" + }, + "filteredPartIds": [ + "StartboardPart-LogsDashboardPart-6d2eff36-636b-406d-8390-12e2135e700a", + "StartboardPart-LogsDashboardPart-6d2eff36-636b-406d-8390-12e2135e700c", + "StartboardPart-LogsDashboardPart-6d2eff36-636b-406d-8390-12e2135e700d", + "StartboardPart-LogsDashboardPart-6d2eff36-636b-406d-8390-12e2135e700e", + "StartboardPart-LogsDashboardPart-6d2eff36-636b-406d-8390-12e2135e700f" + ] + } + } + } + } + } + } + } + ], + "outputs": { + "results": { + "type": "string", + "value": "The Benchmarking job has been triggered successfully. Please check the storage account you provided for Job Status and Results. The jobs status will be available in a storage table within a few minutes and results will be available once the job finishes." + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh new file mode 100644 index 0000000000..2b41d4d0e4 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +#Cloning Test Bench Repo +echo "########## Cloning Test Bench repository ##########" +git clone https://github.com/Azure/azure-cosmos-dotnet-v3.git + +# Build Benchmark Project +cd 'azure-cosmos-dotnet-v3/' +git checkout ${BENCHMARKING_TOOLS_BRANCH_NAME} + +cd 'Microsoft.Azure.Cosmos.Samples/Tools/Benchmark' + +echo "########## Build benckmark tool ##########" +dotnet build --configuration Release -p:"OSSProjectRef=true;ShouldUnsetParentConfigurationAndPlatform=false" + +echo "########## Run benchmark ##########" +nohup dotnet run -c Release -e ${COSMOS_URI} -k ${COSMOS_KEY} -t ${THROUGHPUT} -n ${DOCUMENTS} --pl ${PARALLELISM} \ +--enablelatencypercentiles true --resultscontainer ${RESULTS_CONTAINER} --resultspartitionkeyvalue "pk" \ +--DiagnosticsStorageConnectionString ${DIAGNOSTICS_STORAGE_CONNECTION_STRING} \ +--DiagnosticLatencyThresholdInMs ${DIAGNOSTICS_LATENCY_THRESHOLD_IN_MS} \ +--DiagnosticsStorageContainerPrefix ${DIAGNOSTICS_STORAGE_CONTAINER_PREFIX} \ +--MetricsReportingIntervalInSec ${METRICS_REPORTINT_INTERVAL_SEC} \ +--AppInsightsConnectionString ${APP_INSIGHT_CONN_STR} \ +-w ${WORKLOAD_TYPE} \ +> "/home/${ADMIN_USER_NAME}/agent.out" 2> "/home/${ADMIN_USER_NAME}/agent.err" & diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh new file mode 100644 index 0000000000..cf86202002 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/execute.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +export DOTNET_CLI_HOME=/temp + +cloud-init status --wait +curl -o custom-script.sh $CUSTOM_SCRIPT_URL +bash -x custom-script.sh \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/system/cloud-init.txt b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/system/cloud-init.txt new file mode 100644 index 0000000000..6821dcaa15 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/system/cloud-init.txt @@ -0,0 +1,16 @@ +#cloud-config +package_upgrade: true +packages: + - azure-cli + +runcmd: + - wget https://aka.ms/downloadazcopy-v10-linux + - tar -xvf downloadazcopy-v10-linux + - sudo cp ./azcopy_linux_amd64_*/azcopy /usr/bin/ + - wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb + - sudo dpkg -i packages-microsoft-prod.deb + - sudo apt update + - sudo apt install apt-transport-https -y + - sudo apt install dotnet-sdk-6.0 -y + - sudo apt install dotnet-runtime-6.0 + - sudo apt-get install git \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs index f5ff091b28..f69c305096 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs @@ -126,6 +126,16 @@ public class BenchmarkConfig [Option(Required = false, HelpText = "Container to publish results to")] public string ResultsContainer { get; set; } = "runsummary"; + + [Option(Required = false, HelpText = "Request latency threshold for capturing diagnostic data")] + public int DiagnosticLatencyThresholdInMs { get; set; } = 100; + + [Option(Required = false, HelpText = "Blob storage account connection string")] + [JsonIgnore] + public string DiagnosticsStorageConnectionString { get; set; } + + [Option(Required = false, HelpText = "Blob storage container folder prefix")] + public string DiagnosticsStorageContainerPrefix { get; set; } [Option(Required = false, HelpText = "Metrics reporting interval in seconds")] public int MetricsReportingIntervalInSec { get; set; } = 5; diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkLatencyEventSource.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkLatencyEventSource.cs index 309eb58c15..5a945d21da 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkLatencyEventSource.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkLatencyEventSource.cs @@ -36,9 +36,10 @@ public void LatencyDiagnostics( string dbName, string containerName, int durationInMs, - Func lazyDiagnostics) + Func lazyDiagnostics, + int latencyThreshold) { - if (durationInMs > BenchmarkLatencyEventSource.TraceLatencyThreshold + if (durationInMs > latencyThreshold && this.IsEnabled()) { this.WriteEvent(1, dbName, containerName, durationInMs, lazyDiagnostics()); diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkProgress.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkProgress.cs new file mode 100644 index 0000000000..6a7650a5cd --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkProgress.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace CosmosBenchmark +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Newtonsoft.Json; + + /// + /// Represents a class that is used as an item in CosmosDB to track benchmark progress. + /// + public class BenchmarkProgress + { + + /// + /// Record item id + /// + [JsonProperty] + public string id { get; set; } + + /// + /// Machine name + /// + [JsonProperty] + public string MachineName { get; set; } + + /// + /// Job status STARTED|COMPLETED + /// + [JsonProperty] + public string JobStatus { get; set; } + + /// + /// Job start time . + /// + [JsonProperty] + public DateTime JobStartTime { get; set; } + + /// + /// Job end time . + /// + [JsonProperty] + public DateTime JobEndTime { get; set; } + + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj index 2506e48102..073e8daa7a 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/CosmosBenchmark.csproj @@ -17,6 +17,7 @@ + diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/DiagnosticDataListener.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/DiagnosticDataListener.cs new file mode 100644 index 0000000000..1171a05faa --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/DiagnosticDataListener.cs @@ -0,0 +1,224 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace CosmosBenchmark.Fx +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.Tracing; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using Azure.Storage.Blobs; + + public class DiagnosticDataListener : EventListener + { + /// + /// A constant string representing the container name in Azure Blob Storage. + /// + private const string BlobContainerName = "diagnostics"; + + /// + /// A constant string representing the diagnostics file path. + /// + public const string DiagnosticsFileName = "BenchmarkDiagnostics.out"; + + /// + /// A constant int representing the maximum file size. + /// + private readonly int MaxDIagnosticFileSize = 100_000_000; + + /// + /// A constant int representing the interval at which the file size is checked. + /// + private readonly TimeSpan FileSizeCheckInterval = TimeSpan.FromSeconds(5); + + /// + /// string representing filename prefix in blob storage + /// + private readonly string BlobPrefix = $"{Environment.MachineName}/{Environment.MachineName}"; + + /// + /// Number of files + /// + private int filesCount = 0; + + /// + /// Represents a Blob storage container client instance + /// + private readonly Lazy BlobContainerClient; + + /// + /// Represents a Benchmark Configs + /// + private readonly BenchmarkConfig config; + + /// + /// Current diagnostics optput StreamWriter + /// + private volatile TextWriter Writer; + + /// + /// Current diagnostics optput filename + /// + public volatile string WriterFileName; + + /// + /// List of all previously opened StreamWriters + /// should be stored for later closing, as they may be + /// concurrently accessed by other threads for appending to a file. + /// + private readonly List TextWriters = new List(); + + /// + /// Represents a class that performs writing diagnostic data to a file and uploading it to Azure Blob Storage + /// + public DiagnosticDataListener(BenchmarkConfig config) + { + this.config = config; + this.EnableEvents(BenchmarkLatencyEventSource.Instance, EventLevel.Informational); + this.BlobContainerClient = new Lazy(() => this.GetBlobServiceClient()); + this.Writer = TextWriter.Synchronized(File.AppendText(DiagnosticsFileName)); + this.WriterFileName = DiagnosticsFileName; + + /// + /// Checks the file size every milliseconds for diagnostics and creates a new one if the maximum limit is exceeded. + /// + ThreadPool.QueueUserWorkItem(async state => + { + while (true) + { + try + { + if (File.Exists(this.WriterFileName)) + { + FileInfo fileInfo = new FileInfo(this.WriterFileName); + long fileSize = fileInfo.Length; + + if (fileSize > this.MaxDIagnosticFileSize) + { + string newFilePath = Path.Combine(fileInfo.DirectoryName, $"{DiagnosticsFileName}-{this.filesCount}"); + + File.Create(newFilePath).Close(); + + this.TextWriters.Add(this.Writer); + + this.Writer = TextWriter.Synchronized(File.AppendText($"{newFilePath}")); + this.WriterFileName = newFilePath; + this.filesCount++; + + Utility.TeeTraceInformation("File size exceeded 100MB. Created a new one."); + } + } + + await Task.Delay(this.FileSizeCheckInterval); + + this.CloseStreamWriters(); + } + catch (Exception ex) + { + Utility.TraceError("Exception in file size check loop", ex); + } + } + }); + } + + + /// + /// Listening for events generated by BenchmarkLatencyEventSource + /// + /// An instance of containing the request latency and diagnostics. + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + try + { + this.Writer.WriteLine($"{eventData.Payload[2]} ; {eventData.Payload[3]}"); + } + catch (Exception ex) + { + Utility.TraceError("An exception ocured while writing diagnostic data to the file", ex); + } + } + + /// + /// Uploading all files with diagnostic data to blob storage + /// + public void UploadDiagnostcs() + { + Utility.TeeTraceInformation("Uploading diagnostics"); + string[] diagnosticFiles = Directory.GetFiles(".", $"{DiagnosticsFileName}*"); + string containerPrefix = this.config.DiagnosticsStorageContainerPrefix; + + this.CloseStreamWriters(); + this.SafeCloseCurrentStreamWriter(); + + BlobContainerClient blobContainerClient = this.BlobContainerClient.Value; + for (int i = 0; i < diagnosticFiles.Length; i++) + { + try + { + string diagnosticFile = diagnosticFiles[i]; + Utility.TeeTraceInformation($"Uploading {i + 1} of {diagnosticFiles.Length} file: {diagnosticFile} "); + + string blobName = string.IsNullOrEmpty(containerPrefix) ? + $"{this.BlobPrefix}-{i}.out" : $"{containerPrefix}/{this.BlobPrefix}-{i}.out"; + + BlobClient blobClient = blobContainerClient.GetBlobClient(blobName); + + blobClient.Upload(diagnosticFile, overwrite: true); + } + catch (Exception ex) + { + Utility.TraceError($"An exception ocured while uploading file {this.WriterFileName} to the blob storage", ex); + } + } + } + + /// + /// Closes all unclosed StreamWriters + /// + private void CloseStreamWriters() + { + + this.TextWriters.ForEach(t => + { + try + { + t.Close(); + } + catch (Exception ex) + { + Utility.TraceError("An exception ocured while closing StreamWriters", ex); + } + }); + } + + /// + /// Safe close current StreamWriter + /// + private void SafeCloseCurrentStreamWriter() + { + try + { + this.Writer.Close(); + } + catch (Exception ex) + { + Utility.TraceError("An exception ocured while closing StreamWriters.", ex); + } + } + + /// + /// Creating an instance of BlobClient using configs + /// + private BlobContainerClient GetBlobServiceClient() + { + BlobContainerClient blobContainerClient = new BlobContainerClient( + this.config.DiagnosticsStorageConnectionString, + BlobContainerName); + blobContainerClient.CreateIfNotExists(); + return blobContainerClient; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs index 866e400870..1676dd0052 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollector.cs @@ -107,7 +107,7 @@ public MetricsCollector(Meter meter, string prefix) this.successOperationCounter = meter.CreateCounter($"{prefix}OperationSuccess"); this.failureOperationCounter = meter.CreateCounter($"{prefix}OperationFailure"); - + this.latencyInMsMetricNameGauge = this.meter.CreateObservableGauge($"{prefix}OperationLatencyInMs", () => new Measurement(this.latencyInMs)); @@ -140,25 +140,25 @@ public void CollectMetricsOnFailure() /// /// Records success operation latency in milliseconds. /// - /// The number of milliseconds to record. + /// The number of milliseconds to record. public void RecordSuccessOpLatencyAndRps( TimeSpan timeSpan) { - this.rps = 1000 / timeSpan.Milliseconds; - this.latencyInMs = timeSpan.Milliseconds; + this.rps = timeSpan.TotalMilliseconds != 0 ? 1000 / timeSpan.TotalMilliseconds : 0; + this.latencyInMs = timeSpan.TotalMilliseconds; this.rpsMetricNameHistogram.Record(this.rps); this.operationLatencyHistogram.Record(this.latencyInMs); } - + /// /// Records failed operation latency in milliseconds. /// - /// The number of milliseconds to record. + /// The number of milliseconds to record. public void RecordFailedOpLatencyAndRps( TimeSpan timeSpan) { - this.rpsFailed = 1000 / timeSpan.Milliseconds; - this.latencyFailedInMs = timeSpan.Milliseconds; + this.rpsFailed = timeSpan.TotalMilliseconds != 0 ? 1000 / timeSpan.TotalMilliseconds : 0; + this.latencyFailedInMs = timeSpan.TotalMilliseconds; this.rpsFailedMetricNameHistogram.Record(this.rpsFailed); this.operationFailedLatencyHistogram.Record(this.latencyFailedInMs); } diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs index 4de7009be1..f49d9ec571 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/MetricsCollectorProvider.cs @@ -6,6 +6,8 @@ namespace CosmosBenchmark { using System; using System.Diagnostics.Metrics; + using System.Threading; + using System.Threading.Tasks; using OpenTelemetry.Metrics; /// @@ -13,7 +15,10 @@ namespace CosmosBenchmark /// internal class MetricsCollectorProvider { - private readonly MetricCollectionWindow metricCollectionWindow; + private const int WindowCheckInterval = 10; + private MetricCollectionWindow metricCollectionWindow; + + private static readonly object metricCollectionWindowLock = new object(); private readonly MetricsCollector insertOperationMetricsCollector; @@ -36,6 +41,38 @@ public MetricsCollectorProvider(BenchmarkConfig config, MeterProvider meterProvi this.queryOperationMetricsCollector ??= new MetricsCollector(this.queryOperationMeter, "Query"); this.readOperationMetricsCollector ??= new MetricsCollector(this.readOperationMeter, "Read"); this.metricCollectionWindow ??= new MetricCollectionWindow(config); + + /// + /// Flush metrics every + /// + ThreadPool.QueueUserWorkItem(async state => + { + while (true) + { + MetricCollectionWindow metricCollectionWindow = this.GetCurrentMetricCollectionWindow(config); + + // Reset metricCollectionWindow and flush. + if (!metricCollectionWindow.IsValid) + { + this.meterProvider.ForceFlush(); + this.metricCollectionWindow.Reset(config); + } + await Task.Delay(TimeSpan.FromMilliseconds(MetricsCollectorProvider.WindowCheckInterval)); + } + }); + } + + private MetricCollectionWindow GetCurrentMetricCollectionWindow(BenchmarkConfig config) + { + if (this.metricCollectionWindow is null || !this.metricCollectionWindow.IsValid) + { + lock (metricCollectionWindowLock) + { + this.metricCollectionWindow ??= new MetricCollectionWindow(config); + } + } + + return this.metricCollectionWindow; } /// @@ -45,17 +82,8 @@ public MetricsCollectorProvider(BenchmarkConfig config, MeterProvider meterProvi /// Benchmark configuration. /// Metrics collector. /// Thrown if provided benchmark operation is not covered supported to collect metrics. - public IMetricsCollector GetMetricsCollector(IBenchmarkOperation benchmarkOperation, BenchmarkConfig config) + public IMetricsCollector GetMetricsCollector(IBenchmarkOperation benchmarkOperation) { - MetricCollectionWindow metricCollectionWindow = this.metricCollectionWindow; - - // Reset metricCollectionWindow and flush. - if (!metricCollectionWindow.IsValid) - { - this.meterProvider.ForceFlush(); - metricCollectionWindow.Reset(config); - } - return benchmarkOperation.OperationType switch { BenchmarkOperationType.Insert => this.insertOperationMetricsCollector, diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs index 2348d4a995..d32052e3fe 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/ParallelExecutionStrategy.cs @@ -43,6 +43,7 @@ await warmupExecutor.ExecuteAsync( benchmarkConfig, metricsCollectorProvider); + Utility.TeePrint("Starting execution {0} tasks", serialExecutorConcurrency); IExecutor[] executors = new IExecutor[serialExecutorConcurrency]; for (int i = 0; i < serialExecutorConcurrency; i++) { @@ -113,9 +114,8 @@ private async Task LogOutputStats( using (ConsoleColorContext ct = new ConsoleColorContext(ConsoleColor.Green)) { - Console.WriteLine(); - Console.WriteLine("Summary:"); - Console.WriteLine("--------------------------------------------------------------------- "); + Utility.TeeTraceInformation("Summary:"); + Utility.TeeTraceInformation("--------------------------------------------------------------------- "); lastSummary.Print(lastSummary.failedOpsCount + lastSummary.successfulOpsCount); // Skip first 5 and last 5 counters as outliers @@ -128,7 +128,6 @@ private async Task LogOutputStats( if (summaryCounters.Length > 10) { - Console.WriteLine(); Utility.TeeTraceInformation("After Excluding outliers"); runSummary.Top10PercentAverageRps = Math.Round(summaryCounters.Take((int)(0.1 * summaryCounters.Length)).Average(), 0); @@ -160,7 +159,7 @@ private async Task LogOutputStats( Utility.TeeTraceInformation("Please adjust ItemCount high to run of at-least 1M"); } - Console.WriteLine("--------------------------------------------------------------------- "); + Utility.TeeTraceInformation("--------------------------------------------------------------------- "); return runSummary; } diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs index b33218f65b..3bb84e26fa 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/SerialOperationExecutor.cs @@ -50,8 +50,7 @@ public async Task ExecuteAsync( int currentIterationCount = 0; do { - IMetricsCollector metricsCollector = metricsCollectorProvider.GetMetricsCollector(this.operation, benchmarkConfig); - + IMetricsCollector metricsCollector = metricsCollectorProvider.GetMetricsCollector(this.operation); OperationResult? operationResult = null; await this.operation.PrepareAsync(); @@ -115,7 +114,8 @@ public async Task ExecuteAsync( } catch (Exception e) { - Trace.TraceInformation($"Error: {e.Message}"); + Utility.TraceError("Error:", e); + } finally { diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs index fff2acac3e..4f8785860d 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Fx/TelemetrySpan.cs @@ -63,11 +63,11 @@ public void Dispose() if(this.isFailed) { - this.recordSuccessOpLatencyAction?.Invoke(TimeSpan.FromMilliseconds(this.stopwatch.Elapsed.TotalMilliseconds)); + this.recordSuccessOpLatencyAction?.Invoke(this.stopwatch.Elapsed); } else { - this.recordSuccessOpLatencyAction?.Invoke(TimeSpan.FromMilliseconds(this.stopwatch.Elapsed.TotalMilliseconds)); + this.recordSuccessOpLatencyAction?.Invoke(this.stopwatch.Elapsed); } } @@ -76,7 +76,9 @@ public void Dispose() operationResult.DatabseName, operationResult.ContainerName, (int)this.stopwatch.ElapsedMilliseconds, - operationResult.LazyDiagnostics); + operationResult.LazyDiagnostics, + this.benchmarkConfig.DiagnosticLatencyThresholdInMs); + } } diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs index 2e7687932f..744d53f2af 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Program.cs @@ -7,6 +7,7 @@ namespace CosmosBenchmark using System; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.Tracing; using System.IO; using System.Linq; using System.Net; @@ -15,6 +16,7 @@ namespace CosmosBenchmark using System.Threading; using System.Threading.Tasks; using Azure.Monitor.OpenTelemetry.Exporter; + using CosmosBenchmark.Fx; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; @@ -44,6 +46,12 @@ public static async Task Main(string[] args) ThreadPool.SetMinThreads(config.MinThreadPoolSize, config.MinThreadPoolSize); + DiagnosticDataListener diagnosticDataListener = null; + if (!string.IsNullOrEmpty(config.DiagnosticsStorageConnectionString)) + { + diagnosticDataListener = new DiagnosticDataListener(config); + } + if (config.EnableLatencyPercentiles) { TelemetrySpan.IncludePercentile = true; @@ -55,13 +63,22 @@ public static async Task Main(string[] args) Program program = new Program(); RunSummary runSummary = await program.ExecuteAsync(config, metricsCollectorProvider); + + if (!string.IsNullOrEmpty(config.DiagnosticsStorageConnectionString)) + { + diagnosticDataListener.UploadDiagnostcs(); + } + } + catch (Exception e) + { + Utility.TeeTraceInformation("Exception ocured:" + e.ToString()); } finally { - Console.WriteLine($"{nameof(CosmosBenchmark)} completed successfully."); + Utility.TeeTraceInformation($"{nameof(CosmosBenchmark)} completed successfully."); if (Debugger.IsAttached) { - Console.WriteLine("Press any key to exit..."); + Utility.TeeTraceInformation("Press any key to exit..."); Console.ReadLine(); } } @@ -113,11 +130,11 @@ private static async Task AddAzureInfoToRunSummary() JObject jObject = JObject.Parse(jsonVmInfo); RunSummary.AzureVmInfo = jObject; RunSummary.Location = jObject["compute"]["location"].ToString(); - Console.WriteLine($"Azure VM Location:{RunSummary.Location}"); + Utility.TeeTraceInformation($"Azure VM Location:{RunSummary.Location}"); } - catch(Exception e) + catch (Exception e) { - Console.WriteLine("Failed to get Azure VM info:" + e.ToString()); + Utility.TeeTraceInformation("Failed to get Azure VM info:" + e.ToString()); } } @@ -151,11 +168,15 @@ private async Task ExecuteAsync(BenchmarkConfig config, $"Container {config.Container} must have a configured throughput."); } - Console.WriteLine($"Using container {config.Container} with {currentContainerThroughput} RU/s"); + Container resultContainer = await GetResultContainer(config, cosmosClient); + + BenchmarkProgress benchmarkProgressItem = await CreateBenchmarkProgressItem(resultContainer); + + Utility.TeeTraceInformation($"Using container {config.Container} with {currentContainerThroughput} RU/s"); int taskCount = config.GetTaskCount(currentContainerThroughput.Value); - Console.WriteLine("Starting Inserts with {0} tasks", taskCount); - Console.WriteLine(); + Utility.TeePrint("Starting Inserts with {0} tasks", taskCount); + string partitionKeyPath = containerResponse.Resource.PartitionKeyPath; int opsPerTask = config.ItemCount / taskCount; @@ -184,7 +205,7 @@ private async Task ExecuteAsync(BenchmarkConfig config, if (config.CleanupOnFinish) { - Console.WriteLine($"Deleting Database {config.Database}"); + Utility.TeeTraceInformation($"Deleting Database {config.Database}"); await database.DeleteStreamAsync(); } @@ -196,8 +217,11 @@ private async Task ExecuteAsync(BenchmarkConfig config, } runSummary.ConsistencyLevel = consistencyLevel; + + BenchmarkProgress benchmarkProgress = await CompleteBenchmarkProgressStatus(benchmarkProgressItem, resultContainer); if (config.PublishResults) { + Utility.TeeTraceInformation("Publishing results"); runSummary.Diagnostics = CosmosDiagnosticsLogger.GetDiagnostics(); await this.PublishResults( config, @@ -228,6 +252,7 @@ private async Task PublishResults( Container resultContainer = cosmosClient.GetContainer(config.ResultsDatabase, config.ResultsContainer); await resultContainer.CreateItemAsync(runSummary, new PartitionKey(runSummary.pk)); } + } private Func GetBenchmarkFactory( @@ -301,24 +326,60 @@ private static async Task CreatePartitionedContainerAsync(Ben { Microsoft.Azure.Cosmos.Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(options.Database); - Container container = database.GetContainer(options.Container); + // Show user cost of running this test + double estimatedCostPerMonth = 0.06 * options.Throughput; + double estimatedCostPerHour = estimatedCostPerMonth / (24 * 30); + Utility.TeeTraceInformation($"The container will cost an estimated ${Math.Round(estimatedCostPerHour, 2)} per hour (${Math.Round(estimatedCostPerMonth, 2)} per month)"); + Utility.TeeTraceInformation("Press enter to continue ..."); + Console.ReadLine(); - try - { - return await container.ReadContainerAsync(); - } - catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + string partitionKeyPath = options.PartitionKeyPath; + return await database.CreateContainerIfNotExistsAsync(options.Container, partitionKeyPath, options.Throughput); + } + + /// + /// Creating a progress item in ComsosDb when the benchmark start + /// + /// An instance of that represents operations performed on a database container. + private static async Task CreateBenchmarkProgressItem(Container resultContainer) + { + BenchmarkProgress benchmarkProgress = new BenchmarkProgress { - // Show user cost of running this test - double estimatedCostPerMonth = 0.06 * options.Throughput; - double estimatedCostPerHour = estimatedCostPerMonth / (24 * 30); - Console.WriteLine($"The container will cost an estimated ${Math.Round(estimatedCostPerHour, 2)} per hour (${Math.Round(estimatedCostPerMonth, 2)} per month)"); - Console.WriteLine("Press enter to continue ..."); - Console.ReadLine(); - - string partitionKeyPath = options.PartitionKeyPath; - return await database.CreateContainerAsync(options.Container, partitionKeyPath, options.Throughput); - } + id = Environment.MachineName, + MachineName = Environment.MachineName, + JobStatus = "STARTED", + JobStartTime = DateTime.Now + }; + + ItemResponse itemResponse = await resultContainer.UpsertItemAsync( + benchmarkProgress, new PartitionKey(benchmarkProgress.id)); + + return itemResponse.Resource; + } + + /// + /// Change a progress item status to Complete in ComsosDb when the benchmark compleated + /// + /// An instance of that represents operations performed on a database container. + /// An instance of that represents the document to be modified. + public static async Task CompleteBenchmarkProgressStatus(BenchmarkProgress benchmarkProgress, Container resultContainer) + { + benchmarkProgress.JobStatus = "COMPLETED"; + benchmarkProgress.JobEndTime = DateTime.Now; + ItemResponse itemResponse = await resultContainer.UpsertItemAsync(benchmarkProgress); + return itemResponse.Resource; + } + + /// + /// Configure and prepare the Cosmos DB Container instance for the result container. + /// + /// An instance of containing the benchmark tool input parameters. + /// An instance of that represents operations performed on a CosmosDb database. + private static async Task GetResultContainer(BenchmarkConfig config, CosmosClient cosmosClient) + { + Database database = cosmosClient.GetDatabase(config.ResultsDatabase ?? config.Database); + ContainerResponse containerResponse = await database.CreateContainerIfNotExistsAsync(id: config.ResultsContainer, partitionKeyPath: "/id"); + return containerResponse.Container; } private static void ClearCoreSdkListeners() diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md index 8aac52daad..5a2bac1937 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/README.md @@ -54,6 +54,7 @@ cd 'azure-cosmos-dotnet-v3/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark' dotnet build --configuration Release -p:"OSSProjectRef=true;ShouldUnsetParentConfigurationAndPlatform=false" ``` + For PerfRuns with reports (INTERNAL) ``` @@ -127,3 +128,7 @@ Copyright (C) 2019 CosmosBenchmark ## Running on Azure If you want to quickly get results, you can use our [guide to leverage Azure Container Instances](./AzureContainerInstances/README.md) to execute the benchmarks in any number of Azure regions with very little setup required. + +## Running on Azure VM + +If you want to execute benchmarking on multiple machines with one ARM Template, you can use our [guide to leverage Azure Virtual Machines](./AzureVmBenchmark/README.md). diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Utility.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Utility.cs index d743bd391d..6d278f3d3a 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Utility.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/Utility.cs @@ -16,6 +16,12 @@ public static void TeeTraceInformation(string payload) Trace.TraceInformation(payload); } + public static void TraceError(string payload, Exception e) + { + Console.WriteLine($"{payload}: {e.Message} {e.StackTrace}"); + Trace.TraceError(payload); + } + public static void TeePrint(string format, params object[] arg) { string payload = string.Format(format, arg); diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/CosmosBenchmarkTests.csproj b/Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/CosmosBenchmarkTests.csproj new file mode 100644 index 0000000000..7da26827a5 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/CosmosBenchmarkTests.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/Fx/DiagnosticDataListenerTests.cs b/Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/Fx/DiagnosticDataListenerTests.cs new file mode 100644 index 0000000000..9c453cf2e6 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Samples/Tools/CosmosBenchmarkTests/Fx/DiagnosticDataListenerTests.cs @@ -0,0 +1,54 @@ +namespace CosmosBenchmark.Fx.Tests +{ + using CosmosBenchmark.Fx; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.IO; + using Moq; + using Azure.Storage.Blobs; + + [TestClass] + public class DiagnosticDataListenerTests + { + private DiagnosticDataListener listener; + + [TestInitialize] + public void Setup() + { + this.listener = new DiagnosticDataListener(); + } + + [TestCleanup] + public void Cleanup() + { + string[] diagnosticFiles = Directory.GetFiles(".", "BenchmarkDiagnostics.out*"); + foreach (string file in diagnosticFiles) + { + File.Delete(file); + } + } + + + /// + /// The scenario tested here is to ensure + /// that all created files with captured diagnostic + /// data are successfully uploaded to the Blob storage + /// + [TestMethod] + public void UploadDiagnostics_WhenFilesExist_ShouldUploadFilesToBlobStorage() + { + for (int i = 0; i < 10; i++) + { + string fileName = $"BenchmarkDiagnostics.out-{i}"; + File.Create(fileName).Close(); + } + int filesCount = Directory.GetFiles(".", $"{DiagnosticDataListener.DiagnosticsFileName}*").Length; + + Mock mockContainer = new Mock(); + Mock mockClient = new Mock(); + mockContainer.Setup(mock => mock.GetBlobClient(It.IsAny())).Returns(mockClient.Object); + + this.listener.UploadDiagnostcs(mockContainer.Object, "prefix"); + mockClient.Verify(mock => mock.Upload(It.IsAny(), true, default), Times.Exactly(filesCount)); + } + } +} From 3aced24beac2bc63f0ad84944bcb87ea3429d9a6 Mon Sep 17 00:00:00 2001 From: David Chaava Date: Tue, 29 Aug 2023 21:04:53 +0200 Subject: [PATCH 19/20] [Internal] Benchmark tool: Adds feature to the dashboard that generate plots queries for metrics with a workload name prefix, depending on the benchmark workload type. #4048 (#4053) * Merge remote-tracking branch 'origin/master' into users/v-dchaava/benchmark-diagnostics/3889 * modified: Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/README.md * add metrics prefixes * fix chart metrics names * fix dashboard queries according selected workload type --------- Co-authored-by: David Chaava --- .../Benchmark/AzureVmBenchmark/azuredeploy.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json index 347857cf82..594d4c9756 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/azuredeploy.json @@ -209,10 +209,11 @@ "customScriptUrl": "[concat('https://raw.githubusercontent.com/Azure/azure-cosmos-dotnet-v3/',parameters('benchmarkingToolsBranchName'),'/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/AzureVmBenchmark/scripts/custom-script.sh')]", "vmScriptExtensionScriptName": "execute.sh", "convertedDatetime": "[dateTimeFromEpoch(parameters('startDate'))]", + "metricsPrefix" : "[if(startsWith(parameters('workloadType'), 'Read'), 'Read', if(startsWith(parameters('workloadType'), 'Query'), 'Query', if(startsWith(parameters('workloadType'), 'Insert'), 'Insert', '')))]", "appInsightsResourceIds": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/microsoft.insights/components/', parameters('applicationInsightsName'))]", - "chart0Expression": "[concat('customMetrics\n| where name == \"ReadOperationLatencyInMs\" and timestamp > ago(1d)\n| summarize\n percentile(value, 50),\n percentile(value, 75),\n percentile(value, 90),\n percentile(value, 95)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]", - "chart1Expression": "[concat('customMetrics\n| where name == \"ReadOperationLatencyInMs\" and timestamp > ago(1d)\n| summarize\n percentile(value, 99),\n percentile(value, 99.9),\n percentile(value, 99.99)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]", - "chart2Expression": "[concat('customMetrics\n| where name == \"ReadOperationRps\" and timestamp > ago(1d)\n| summarize\n avg(value)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]" + "chart0Expression": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationLatencyInMs\" and timestamp > ago(1d)\n| summarize\n percentile(value, 50),\n percentile(value, 75),\n percentile(value, 90),\n percentile(value, 95)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]", + "chart1Expression": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationLatencyInMs\" and timestamp > ago(1d)\n| summarize\n percentile(value, 99),\n percentile(value, 99.9),\n percentile(value, 99.99)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]", + "chart2Expression": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationRps\" and timestamp > ago(1d)\n| summarize\n avg(value)\n by ts = bin(timestamp, ', parameters('metricsReportingIntervalInSec'), 's)\n| render timechart \n\n')]" }, "resources": [ @@ -776,7 +777,7 @@ }, { "name": "Query", - "value": "customMetrics\n| where name == \"ReadOperationFailure\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n", + "value": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationFailure\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n')]", "isOptional": true }, { @@ -834,7 +835,7 @@ "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", "settings": { "content": { - "Query": "customMetrics\n| where name == \"ReadOperationFailure\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n\n", + "Query": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationFailure\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n\n')]", "Dimensions": { "xAxis": { "name": "ts", @@ -903,7 +904,7 @@ }, { "name": "Query", - "value": "customMetrics\n| where name == \"ReadOperationSuccess\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n", + "value": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationSuccess\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n')]", "isOptional": true }, { @@ -961,7 +962,7 @@ "type": "Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart", "settings": { "content": { - "Query": "customMetrics\n| where name == \"ReadOperationSuccess\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n\n", + "Query": "[concat('customMetrics\n| where name == \"', variables('metricsPrefix'), 'OperationSuccess\" and timestamp > ago(1d)\n| summarize\n sum(value)\n by ts = bin(timestamp, 1s)\n | render timechart\n\n')]", "Dimensions": { "xAxis": { "name": "ts", From c72ebc85746ea0c0c0ee3689e83ad23027751e2a Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Thu, 31 Aug 2023 23:25:30 +0530 Subject: [PATCH 20/20] [Internal] Client Telemetry: Adds client config api call to get latest flag status (#4050) * first draft * tets fix * fix dependent projects * reduce refresh time in tests * fix tests and added comments * fix diagnostic handler fix * fix test * adding test * ret pushmove console * fix test * provide options to enable/disable this featire in benchmark and ctl proj * updated trace message Co-authored-by: Matias Quaranta * remove import * updated traces Co-authored-by: Matias Quaranta * test fix * remove null assignment * fix test --------- Co-authored-by: Matias Quaranta --- .../Tools/Benchmark/BenchmarkConfig.cs | 40 ++----- .../Tools/CTL/CTLConfig.cs | 11 +- .../Tools/CTL/Program.cs | 14 --- .../src/ConnectionPolicy.cs | 5 +- .../src/Handler/DiagnosticsHandler.cs | 4 +- .../src/Handler/DiagnosticsHandlerHelper.cs | 59 +++++++--- .../Settings/AccountClientConfiguration.cs | 29 +++++ .../Settings/ClientTelemetryConfiguration.cs | 27 +++++ .../src/Telemetry/ClientTelemetry.cs | 16 ++- .../src/Telemetry/ClientTelemetryOptions.cs | 67 +---------- .../src/Telemetry/ClientTelemetryProcessor.cs | 12 +- .../src/Telemetry/TelemetryToServiceHelper.cs | 110 ++++++++++++++---- .../ClientSideRequestStatisticsTraceDatum.cs | 2 +- .../ClientTelemetryReleaseTests.cs | 11 +- .../ClientTelemetryTests.cs | 27 ++++- .../ClientTelemetryTestsBase.cs | 74 ++++-------- .../SynchronizationContextTests.cs | 43 +++++-- .../Utils/HttpHandlerHelper.cs | 2 + .../Utils/Util.cs | 14 --- .../Mocks/HttpHandlerHelper.cs | 67 +++++++++++ .../Mocks/MockDocumentClient.cs | 42 ++++++- .../DiagnosticHandlerHelperTests.cs | 75 ++++++++++++ .../Telemetry/ClientTelemetryTests.cs | 22 +--- 23 files changed, 500 insertions(+), 273 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/src/Resource/Settings/AccountClientConfiguration.cs create mode 100644 Microsoft.Azure.Cosmos/src/Resource/Settings/ClientTelemetryConfiguration.cs create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/HttpHandlerHelper.cs create mode 100644 Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Diagnostics/DiagnosticHandlerHelperTests.cs diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs index f69c305096..e3ec5c23bb 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/Benchmark/BenchmarkConfig.cs @@ -10,6 +10,7 @@ namespace CosmosBenchmark using System.Linq; using System.Runtime; using CommandLine; + using Microsoft.Azure.Cosmos.Telemetry; using Microsoft.Azure.Documents.Client; using Newtonsoft.Json; @@ -102,18 +103,12 @@ public class BenchmarkConfig [Option(Required = false, HelpText = "Disable core SDK logging")] public bool DisableCoreSdkLogging { get; set; } - [Option(Required = false, HelpText = "Enable Client Telemetry")] - public bool EnableTelemetry { get; set; } - [Option(Required = false, HelpText = "Enable Distributed Tracing")] public bool EnableDistributedTracing { get; set; } [Option(Required = false, HelpText = "Client Telemetry Schedule in Seconds")] public int TelemetryScheduleInSec { get; set; } - [Option(Required = false, HelpText = "Client Telemetry Endpoint")] - public string TelemetryEndpoint { get; set; } - [Option(Required = false, HelpText = "Endpoint to publish results to")] public string ResultsEndpoint { get; set; } @@ -143,6 +138,9 @@ public class BenchmarkConfig [Option(Required = false, HelpText = "Application Insights connection string")] public string AppInsightsConnectionString { get; set; } + [Option(Required = false, HelpText = "Enable Client Telemetry Feature in SDK. Make sure you enable it from the portal also.")] + public bool EnableClientTelemetry { get; set; } = true; + internal int GetTaskCount(int containerThroughput) { int taskCount = this.DegreeOfParallelism; @@ -210,35 +208,21 @@ private string GetUserAgentPrefix() internal Microsoft.Azure.Cosmos.CosmosClient CreateCosmosClient(string accountKey) { + // Overwrite the default timespan if configured + if(this.TelemetryScheduleInSec > 0) + { + ClientTelemetryOptions.DefaultIntervalForTelemetryJob = TimeSpan.FromSeconds(this.TelemetryScheduleInSec); + } + Microsoft.Azure.Cosmos.CosmosClientOptions clientOptions = new Microsoft.Azure.Cosmos.CosmosClientOptions() { ApplicationName = this.GetUserAgentPrefix(), MaxRetryAttemptsOnRateLimitedRequests = 0, MaxRequestsPerTcpConnection = this.MaxRequestsPerTcpConnection, - MaxTcpConnectionsPerEndpoint = this.MaxTcpConnectionsPerEndpoint + MaxTcpConnectionsPerEndpoint = this.MaxTcpConnectionsPerEndpoint, + EnableClientTelemetry = this.EnableClientTelemetry }; - if (this.EnableTelemetry) - { - Environment.SetEnvironmentVariable( - Microsoft.Azure.Cosmos.Telemetry.ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, - "true"); - - if (this.TelemetryScheduleInSec > 0) - { - Environment.SetEnvironmentVariable( - Microsoft.Azure.Cosmos.Telemetry.ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, - Convert.ToString(this.TelemetryScheduleInSec)); - } - - if (!string.IsNullOrEmpty(this.TelemetryEndpoint)) - { - Environment.SetEnvironmentVariable( - Microsoft.Azure.Cosmos.Telemetry.ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, - this.TelemetryEndpoint); - } - } - if (!string.IsNullOrWhiteSpace(this.ConsistencyLevel)) { clientOptions.ConsistencyLevel = (Microsoft.Azure.Cosmos.ConsistencyLevel)Enum.Parse(typeof(Microsoft.Azure.Cosmos.ConsistencyLevel), this.ConsistencyLevel, ignoreCase: true); diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/CTL/CTLConfig.cs b/Microsoft.Azure.Cosmos.Samples/Tools/CTL/CTLConfig.cs index 05064003ce..98bba7b700 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/CTL/CTLConfig.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/CTL/CTLConfig.cs @@ -97,18 +97,15 @@ public string DiagnosticsThresholdDuration [Option("ctl_logging_context", Required = false, HelpText = "Defines a custom context to use on metrics")] public string LogginContext { get; set; } = string.Empty; - [Option("ctl_telemetry_endpoint", Required = false, HelpText = "telemetry juno end point")] - public string TelemetryEndpoint { get; set; } - - [Option("ctl_telemetry_schedule_in_sec", Required = false, HelpText = "telemetry task schedule time in sec")] - public string TelemetryScheduleInSeconds { get; set; } - [Option("ctl_reservoir_type", Required = false, HelpText = "Defines the reservoir type. Valid values are: Uniform, SlidingWindow and ExponentialDecay. The default value is SlidingWindow.")] public ReservoirTypes ReservoirType { get; set; } = ReservoirTypes.SlidingWindow; [Option("ctl_reservoir_sample_size", Required = false, HelpText = "The reservoir sample size.")] public int ReservoirSampleSize { get; set; } = 1028; + [Option("ctl_enable_client_telemetry", Required = false, HelpText = "Enable Client Telemetry Feature in SDK. Make sure you enable it from the portal also.")] + public bool EnableClientTelemetry { get; set; } = true; + internal TimeSpan RunningTimeDurationAsTimespan { get; private set; } = TimeSpan.FromHours(10); internal TimeSpan DiagnosticsThresholdDurationAsTimespan { get; private set; } = TimeSpan.FromSeconds(60); @@ -133,7 +130,7 @@ internal CosmosClient CreateCosmosClient() CosmosClientOptions clientOptions = new CosmosClientOptions() { ApplicationName = CTLConfig.UserAgentSuffix, - EnableClientTelemetry = true + EnableClientTelemetry = this.EnableClientTelemetry }; if (this.UseGatewayMode) diff --git a/Microsoft.Azure.Cosmos.Samples/Tools/CTL/Program.cs b/Microsoft.Azure.Cosmos.Samples/Tools/CTL/Program.cs index 62f1eb913f..b42f651df1 100644 --- a/Microsoft.Azure.Cosmos.Samples/Tools/CTL/Program.cs +++ b/Microsoft.Azure.Cosmos.Samples/Tools/CTL/Program.cs @@ -29,8 +29,6 @@ public static async Task Main(string[] args) { CTLConfig config = CTLConfig.From(args); - SetEnvironmentVariables(config); - if (config.OutputEventTraces) { EnableTraceSourcesToConsole(); @@ -54,12 +52,6 @@ await scenario.InitializeAsync( logger.LogInformation("Initialization completed."); - if(client.ClientOptions.EnableClientTelemetry.GetValueOrDefault()) { - logger.LogInformation("Telemetry is enabled for CTL."); - } else { - logger.LogInformation("Telemetry is disabled for CTL."); - } - List tasks = new List { scenario.RunAsync( @@ -148,12 +140,6 @@ await scenario.InitializeAsync( } } - private static void SetEnvironmentVariables(CTLConfig config) - { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, config.TelemetryEndpoint); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, config.TelemetryScheduleInSeconds); - } - private static IMetricsRoot ConfigureReporting( CTLConfig config, ILogger logger) diff --git a/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs b/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs index c8c1d9db93..8451918e9b 100644 --- a/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs @@ -48,7 +48,7 @@ public ConnectionPolicy() this.MaxConnectionLimit = defaultMaxConcurrentConnectionLimit; this.RetryOptions = new RetryOptions(); this.EnableReadRequestsFallback = null; - this.EnableClientTelemetry = ClientTelemetryOptions.IsClientTelemetryEnabled(); + this.EnableClientTelemetry = false; // by default feature flag is off this.ServerCertificateCustomValidationCallback = null; } @@ -211,6 +211,9 @@ public bool EnableTcpConnectionEndpointRediscovery set; } + /// + /// Gets or sets the flag to enable client telemetry feature. + /// internal bool EnableClientTelemetry { get; diff --git a/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs b/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs index f361987f92..de93fca27b 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandler.cs @@ -4,6 +4,7 @@ namespace Microsoft.Azure.Cosmos.Handlers { + using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Handler; @@ -25,7 +26,8 @@ public override async Task SendAsync( ResponseMessage responseMessage = await base.SendAsync(request, cancellationToken); // Record the diagnostics on the response to get the CPU of when the request was executing - SystemUsageHistory systemUsageHistory = DiagnosticsHandlerHelper.Instance.GetDiagnosticsSystemHistory(); + SystemUsageHistory systemUsageHistory = DiagnosticsHandlerHelper.GetInstance().GetDiagnosticsSystemHistory(); + if (systemUsageHistory != null) { request.Trace.AddDatum( diff --git a/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandlerHelper.cs b/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandlerHelper.cs index 1007234125..cdc3c2bac3 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandlerHelper.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandlerHelper.cs @@ -8,7 +8,6 @@ namespace Microsoft.Azure.Cosmos.Handler using System.Collections.Generic; using Documents.Rntbd; using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Cosmos.Telemetry; /// /// This is a helper class that creates a single static instance to avoid each @@ -22,34 +21,69 @@ internal class DiagnosticsHandlerHelper private const string Telemetrykey = "telemetry"; public static readonly TimeSpan DiagnosticsRefreshInterval = TimeSpan.FromSeconds(10); - private readonly SystemUsageRecorder diagnosticSystemUsageRecorder = new SystemUsageRecorder( + private static readonly SystemUsageRecorder DiagnosticSystemUsageRecorder = new SystemUsageRecorder( identifier: Diagnostickey, historyLength: 6, refreshInterval: DiagnosticsHandlerHelper.DiagnosticsRefreshInterval); private static readonly TimeSpan ClientTelemetryRefreshInterval = TimeSpan.FromSeconds(5); - private readonly SystemUsageRecorder telemetrySystemUsageRecorder = new SystemUsageRecorder( + private static readonly SystemUsageRecorder TelemetrySystemUsageRecorder = new SystemUsageRecorder( identifier: Telemetrykey, historyLength: 120, refreshInterval: DiagnosticsHandlerHelper.ClientTelemetryRefreshInterval); - private static bool isDiagnosticsMonitoringEnabled = false; - private static bool isTelemetryMonitoringEnabled = false; - /// /// Singleton to make sure only one instance of DiagnosticHandlerHelper is there. /// The system usage collection is disabled for internal builds so it is set to null to avoid /// compute for accidentally creating an instance or trying to use it. /// - public static readonly DiagnosticsHandlerHelper Instance = + private static DiagnosticsHandlerHelper Instance = #if INTERNAL null; #else new DiagnosticsHandlerHelper(); #endif + private static bool isDiagnosticsMonitoringEnabled; + private static bool isTelemetryMonitoringEnabled; + private readonly SystemUsageMonitor systemUsageMonitor = null; + public static DiagnosticsHandlerHelper GetInstance() + { + return DiagnosticsHandlerHelper.Instance; + } + + /// + /// Restart the monitor with client telemetry recorder if telemetry is enabled + /// + /// + public static void Refresh(bool isClientTelemetryEnabled) + { + if (isClientTelemetryEnabled != DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled) + { + DiagnosticsHandlerHelper.Instance.StopSystemMonitor(); + + // Update telemetry flag + DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled = isClientTelemetryEnabled; + + // Create new instance, it will start a new system monitor job + DiagnosticsHandlerHelper.Instance = new DiagnosticsHandlerHelper(); + } + } + + private void StopSystemMonitor() + { + try + { + this.systemUsageMonitor?.Dispose(); + } + catch (ObjectDisposedException ex) + { + DefaultTrace.TraceError($"Error while stopping system usage monitor. {0} ", ex); + } + } + /// /// Start System Usage Monitor with Diagnostic and Telemetry Recorder if Telemetry is enabled /// Otherwise Start System Usage Monitor with only Diagnostic Recorder @@ -61,16 +95,14 @@ private DiagnosticsHandlerHelper() // If the CPU monitor fails for some reason don't block the application try { - DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled = ClientTelemetryOptions.IsClientTelemetryEnabled(); - List recorders = new List() { - this.diagnosticSystemUsageRecorder, + DiagnosticsHandlerHelper.DiagnosticSystemUsageRecorder, }; if (DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled) { - recorders.Add(this.telemetrySystemUsageRecorder); + recorders.Add(DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder); } this.systemUsageMonitor = SystemUsageMonitor.CreateAndStart(recorders); @@ -82,7 +114,6 @@ private DiagnosticsHandlerHelper() DefaultTrace.TraceError(ex.Message); DiagnosticsHandlerHelper.isDiagnosticsMonitoringEnabled = false; - DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled = false; } } @@ -99,7 +130,7 @@ public SystemUsageHistory GetDiagnosticsSystemHistory() try { - return this.diagnosticSystemUsageRecorder.Data; + return DiagnosticsHandlerHelper.DiagnosticSystemUsageRecorder.Data; } catch (Exception ex) { @@ -123,7 +154,7 @@ public SystemUsageHistory GetClientTelemetrySystemHistory() try { - return this.telemetrySystemUsageRecorder.Data; + return DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder.Data; } catch (Exception ex) { diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountClientConfiguration.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountClientConfiguration.cs new file mode 100644 index 0000000000..fd7d125dac --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountClientConfiguration.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System.Collections.Generic; + using Microsoft.Azure.Documents; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + internal class AccountClientConfiguration + { + [JsonProperty(PropertyName = Constants.Properties.ClientTelemetryConfiguration)] + public ClientTelemetryConfiguration ClientTelemetryConfiguration { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + + internal bool IsClientTelemetryEnabled() + { + return this.ClientTelemetryConfiguration.IsEnabled && this.ClientTelemetryConfiguration.Endpoint != null; + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientTelemetryConfiguration.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientTelemetryConfiguration.cs new file mode 100644 index 0000000000..3229b69fa2 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientTelemetryConfiguration.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System.Collections.Generic; + using Microsoft.Azure.Documents; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + internal class ClientTelemetryConfiguration + { + [JsonProperty(PropertyName = Constants.Properties.ClientTelemetryEnabled)] + public bool IsEnabled { get; set; } + + [JsonProperty(PropertyName = Constants.Properties.ClientTelemetryEndpoint)] + public string Endpoint { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs index 2dfcbab496..8460998711 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetry.cs @@ -26,11 +26,12 @@ namespace Microsoft.Azure.Cosmos.Telemetry /// internal class ClientTelemetry : IDisposable { - private static readonly TimeSpan observingWindow = ClientTelemetryOptions.GetScheduledTimeSpan(); + private static readonly TimeSpan observingWindow = ClientTelemetryOptions.DefaultIntervalForTelemetryJob; private readonly ClientTelemetryProperties clientTelemetryInfo; private readonly ClientTelemetryProcessor processor; private readonly DiagnosticsHandlerHelper diagnosticsHelper; + private readonly string endpointUrl; private readonly NetworkDataRecorder networkDataRecorder; private readonly CancellationTokenSource cancellationTokenSource; @@ -63,6 +64,7 @@ internal ClientTelemetry() /// /// /// + /// /// ClientTelemetry public static ClientTelemetry CreateAndStartBackgroundTelemetry( string clientId, @@ -72,7 +74,8 @@ public static ClientTelemetry CreateAndStartBackgroundTelemetry( AuthorizationTokenProvider authorizationTokenProvider, DiagnosticsHandlerHelper diagnosticsHelper, IReadOnlyList preferredRegions, - GlobalEndpointManager globalEndpointManager) + GlobalEndpointManager globalEndpointManager, + string endpointUrl ) { DefaultTrace.TraceInformation("Initiating telemetry with background task."); @@ -84,7 +87,8 @@ public static ClientTelemetry CreateAndStartBackgroundTelemetry( authorizationTokenProvider, diagnosticsHelper, preferredRegions, - globalEndpointManager); + globalEndpointManager, + endpointUrl); clientTelemetry.StartObserverTask(); @@ -99,9 +103,12 @@ internal ClientTelemetry( AuthorizationTokenProvider authorizationTokenProvider, DiagnosticsHandlerHelper diagnosticsHelper, IReadOnlyList preferredRegions, - GlobalEndpointManager globalEndpointManager) + GlobalEndpointManager globalEndpointManager, + string endpointUrl) { this.diagnosticsHelper = diagnosticsHelper ?? throw new ArgumentNullException(nameof(diagnosticsHelper)); + this.endpointUrl = endpointUrl ?? throw new ArgumentNullException(nameof(endpointUrl)); + this.globalEndpointManager = globalEndpointManager; this.processor = new ClientTelemetryProcessor(httpClient, authorizationTokenProvider); @@ -177,6 +184,7 @@ ConcurrentDictionary cacheRefreshInfo operationInfoSnapshot: operationInfoSnapshot, cacheRefreshInfoSnapshot: cacheRefreshInfoSnapshot, requestInfoSnapshot: requestInfoSnapshot, + endpointUrl: this.endpointUrl, cancellationToken: cancellationToken.Token), cancellationToken.Token); // Initiating Telemetry Data Processor task which will serialize and send telemetry information to Client Telemetry Service diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryOptions.cs b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryOptions.cs index 2aeaadca63..84a47e1081 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryOptions.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryOptions.cs @@ -72,17 +72,13 @@ internal static class ClientTelemetryOptions internal const string IsThreadStarvingName = "SystemPool_IsThreadStarving_True"; internal const string IsThreadStarvingUnit = "Count"; - internal const double DefaultTimeStampInSeconds = 600; internal const double Percentile50 = 50.0; internal const double Percentile90 = 90.0; internal const double Percentile95 = 95.0; internal const double Percentile99 = 99.0; internal const double Percentile999 = 99.9; internal const string DateFormat = "yyyy-MM-ddTHH:mm:ssZ"; - internal const string EnvPropsClientTelemetrySchedulingInSeconds = "COSMOS.CLIENT_TELEMETRY_SCHEDULING_IN_SECONDS"; - internal const string EnvPropsClientTelemetryEnabled = "COSMOS.CLIENT_TELEMETRY_ENABLED"; - internal const string EnvPropsClientTelemetryVmMetadataUrl = "COSMOS.VM_METADATA_URL"; - internal const string EnvPropsClientTelemetryEndpoint = "COSMOS.CLIENT_TELEMETRY_ENDPOINT"; + internal const string EnvPropsClientTelemetryEnvironmentName = "COSMOS.ENVIRONMENT_NAME"; internal static readonly ResourceType AllowedResourceTypes = ResourceType.Document; @@ -99,53 +95,11 @@ internal static class ClientTelemetryOptions internal static readonly List ExcludedStatusCodes = new List { 404, 409, 412 }; internal static readonly int NetworkTelemetrySampleSize = 200; + internal static TimeSpan DefaultIntervalForTelemetryJob = TimeSpan.FromMinutes(10); internal static int PayloadSizeThreshold = 1024 * 1024 * 2; // 2MB internal static TimeSpan ClientTelemetryProcessorTimeOut = TimeSpan.FromMinutes(5); - private static Uri clientTelemetryEndpoint; private static string environmentName; - private static TimeSpan scheduledTimeSpan = TimeSpan.Zero; - - internal static bool IsClientTelemetryEnabled() - { - bool isTelemetryEnabled = ConfigurationManager - .GetEnvironmentVariable(ClientTelemetryOptions - .EnvPropsClientTelemetryEnabled, false); - - DefaultTrace.TraceInformation($"Telemetry Flag is set to {isTelemetryEnabled}"); - - return isTelemetryEnabled; - } - - internal static TimeSpan GetScheduledTimeSpan() - { - if (scheduledTimeSpan.Equals(TimeSpan.Zero)) - { - double scheduledTimeInSeconds = ClientTelemetryOptions.DefaultTimeStampInSeconds; - try - { - scheduledTimeInSeconds = ConfigurationManager - .GetEnvironmentVariable( - ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, - ClientTelemetryOptions.DefaultTimeStampInSeconds); - - if (scheduledTimeInSeconds <= 0) - { - throw new ArgumentException("Telemetry Scheduled time can not be less than or equal to 0."); - } - } - catch (Exception ex) - { - DefaultTrace.TraceError($"Error while getting telemetry scheduling configuration : {ex.Message}. Falling back to default configuration i.e. {scheduledTimeInSeconds}" ); - } - - scheduledTimeSpan = TimeSpan.FromSeconds(scheduledTimeInSeconds); - - DefaultTrace.TraceInformation($"Telemetry Scheduled in Seconds {scheduledTimeSpan.TotalSeconds}"); - - } - return scheduledTimeSpan; - } internal static string GetHostInformation(Compute vmInformation) { @@ -155,23 +109,6 @@ internal static string GetHostInformation(Compute vmInformation) vmInformation?.AzEnvironment); } - internal static Uri GetClientTelemetryEndpoint() - { - if (clientTelemetryEndpoint == null) - { - string uriProp = ConfigurationManager - .GetEnvironmentVariable( - ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, null); - if (!String.IsNullOrEmpty(uriProp)) - { - clientTelemetryEndpoint = new Uri(uriProp); - } - - DefaultTrace.TraceInformation($"Telemetry Endpoint URL is {uriProp}"); - } - return clientTelemetryEndpoint; - } - internal static string GetEnvironmentName() { if (String.IsNullOrEmpty(environmentName)) diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryProcessor.cs b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryProcessor.cs index c9511cb4e4..ffa74c2466 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryProcessor.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/ClientTelemetryProcessor.cs @@ -19,8 +19,6 @@ namespace Microsoft.Azure.Cosmos.Telemetry internal class ClientTelemetryProcessor { - private static readonly Uri endpointUrl = ClientTelemetryOptions.GetClientTelemetryEndpoint(); - private readonly AuthorizationTokenProvider tokenProvider; private readonly CosmosHttpClient httpClient; @@ -39,6 +37,7 @@ internal async Task ProcessAndSendAsync( ConcurrentDictionary operationInfoSnapshot, ConcurrentDictionary cacheRefreshInfoSnapshot, IReadOnlyList requestInfoSnapshot, + string endpointUrl, CancellationToken cancellationToken) { try @@ -48,7 +47,7 @@ await ClientTelemetryPayloadWriter.SerializedPayloadChunksAsync( operationInfoSnapshot: operationInfoSnapshot, cacheRefreshInfoSnapshot: cacheRefreshInfoSnapshot, sampledRequestInfo: requestInfoSnapshot, - callback: async (payload) => await this.SendAsync(clientTelemetryInfo.GlobalDatabaseAccountName, payload, cancellationToken)); + callback: async (payload) => await this.SendAsync(clientTelemetryInfo.GlobalDatabaseAccountName, payload, endpointUrl, cancellationToken)); } catch (Exception ex) { @@ -67,6 +66,7 @@ await ClientTelemetryPayloadWriter.SerializedPayloadChunksAsync( private async Task SendAsync( string globalDatabaseAccountName, string jsonPayload, + string endpointUrl, CancellationToken cancellationToken) { if (endpointUrl == null) @@ -77,12 +77,12 @@ private async Task SendAsync( try { - DefaultTrace.TraceInformation("Sending Telemetry Data to {0}", endpointUrl.AbsoluteUri); + DefaultTrace.TraceInformation("Sending Telemetry Data to {0}", endpointUrl); using HttpRequestMessage request = new HttpRequestMessage { Method = HttpMethod.Post, - RequestUri = endpointUrl, + RequestUri = new Uri(endpointUrl), Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json") }; @@ -91,7 +91,7 @@ async ValueTask CreateRequestMessage() INameValueCollection headersCollection = new StoreResponseNameValueCollection(); await this.tokenProvider.AddAuthorizationHeaderAsync( headersCollection, - endpointUrl, + new Uri(endpointUrl), "POST", AuthorizationTokenType.PrimaryMasterKey); diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs b/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs index 2f78df0913..9212457951 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs @@ -5,12 +5,17 @@ namespace Microsoft.Azure.Cosmos.Telemetry { using System; + using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Core.Trace; using Microsoft.Azure.Cosmos.Handler; + using Microsoft.Azure.Cosmos.Query.Core.Monads; using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Cosmos.Telemetry.Collector; + using Microsoft.Azure.Cosmos.Tracing; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Collections; internal class TelemetryToServiceHelper : IDisposable { @@ -70,12 +75,69 @@ public static TelemetryToServiceHelper CreateAndInitializeClientConfigAndTelemet TelemetryToServiceHelper helper = new TelemetryToServiceHelper( clientId, connectionPolicy, cosmosAuthorization, httpClient, serviceEndpoint, globalEndpointManager, cancellationTokenSource); - helper.InitializeClientTelemetry(); + _ = helper.RetrieveConfigAndInitiateTelemetryAsync(); // Let it run in backgroud return helper; #endif } + private async Task RetrieveConfigAndInitiateTelemetryAsync() + { + try + { + Uri serviceEndpointWithPath = new Uri(this.serviceEnpoint + Paths.ClientConfigPathSegment); + + TryCatch databaseAccountClientConfigs = await this.GetDatabaseAccountClientConfigAsync(this.cosmosAuthorization, this.httpClient, serviceEndpointWithPath); + if (databaseAccountClientConfigs.Succeeded) + { + this.InitializeClientTelemetry(databaseAccountClientConfigs.Result); + } + else if (!this.cancellationTokenSource.IsCancellationRequested) + { + DefaultTrace.TraceWarning($"Exception while calling client config " + databaseAccountClientConfigs.Exception.ToString()); + } + } + catch (Exception ex) + { + DefaultTrace.TraceWarning($"Exception while running client config job " + ex.ToString()); + } + } + + private async Task> GetDatabaseAccountClientConfigAsync(AuthorizationTokenProvider cosmosAuthorization, + CosmosHttpClient httpClient, + Uri clientConfigEndpoint) + { + INameValueCollection headers = new RequestNameValueCollection(); + await cosmosAuthorization.AddAuthorizationHeaderAsync( + headersCollection: headers, + clientConfigEndpoint, + HttpConstants.HttpMethods.Get, + AuthorizationTokenType.PrimaryMasterKey); + + using (ITrace trace = Trace.GetRootTrace("Account Client Config Read", TraceComponent.Transport, TraceLevel.Info)) + { + try + { + using (HttpResponseMessage responseMessage = await httpClient.GetAsync( + uri: clientConfigEndpoint, + additionalHeaders: headers, + resourceType: ResourceType.DatabaseAccount, + timeoutPolicy: HttpTimeoutPolicyControlPlaneRead.Instance, + clientSideRequestStatistics: null, + cancellationToken: default)) + using (DocumentServiceResponse documentServiceResponse = await ClientExtensions.ParseResponseAsync(responseMessage)) + { + return TryCatch.FromResult(CosmosResource.FromStream(documentServiceResponse)); + } + } + catch (Exception ex) + { + DefaultTrace.TraceWarning($"Exception while calling client config " + ex.StackTrace); + return TryCatch.FromException(ex); + } + } + } + public ITelemetryCollector GetCollector() { return this.collector; @@ -89,28 +151,34 @@ public bool IsClientTelemetryJobRunning() /// /// Trigger Client Telemetry job when it is enabled and not already running. /// - private void InitializeClientTelemetry() + private void InitializeClientTelemetry(AccountClientConfiguration clientConfig) { - try - { - this.clientTelemetry = ClientTelemetry.CreateAndStartBackgroundTelemetry( - clientId: this.clientId, - httpClient: this.httpClient, - userAgent: this.connectionPolicy.UserAgentContainer.BaseUserAgent, - connectionMode: this.connectionPolicy.ConnectionMode, - authorizationTokenProvider: this.cosmosAuthorization, - diagnosticsHelper: DiagnosticsHandlerHelper.Instance, - preferredRegions: this.connectionPolicy.PreferredLocations, - globalEndpointManager: this.globalEndpointManager); - - this.collector = new TelemetryCollector(this.clientTelemetry, this.connectionPolicy); - - DefaultTrace.TraceVerbose("Client Telemetry Enabled."); - } - catch (Exception ex) + DiagnosticsHandlerHelper.Refresh(clientConfig.IsClientTelemetryEnabled()); + + if (clientConfig.IsClientTelemetryEnabled()) { - DefaultTrace.TraceWarning($"Error While starting Telemetry Job : {0}. Hence disabling Client Telemetry", ex.Message); - this.connectionPolicy.EnableClientTelemetry = false; + try + { + this.clientTelemetry = ClientTelemetry.CreateAndStartBackgroundTelemetry( + clientId: this.clientId, + httpClient: this.httpClient, + userAgent: this.connectionPolicy.UserAgentContainer.BaseUserAgent, + connectionMode: this.connectionPolicy.ConnectionMode, + authorizationTokenProvider: this.cosmosAuthorization, + diagnosticsHelper: DiagnosticsHandlerHelper.GetInstance(), + preferredRegions: this.connectionPolicy.PreferredLocations, + globalEndpointManager: this.globalEndpointManager, + endpointUrl: clientConfig.ClientTelemetryConfiguration.Endpoint); + + this.collector = new TelemetryCollector(this.clientTelemetry, this.connectionPolicy); + + DefaultTrace.TraceVerbose("Client Telemetry Enabled."); + } + catch (Exception ex) + { + DefaultTrace.TraceWarning($"Error While starting Telemetry Job : {0}. Hence disabling Client Telemetry", ex); + this.connectionPolicy.EnableClientTelemetry = false; + } } } diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs index def12c8262..829899c4b1 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs @@ -419,7 +419,7 @@ public void UpdateSystemUsage() this.systemUsageHistory.Values.Count == 0 || this.systemUsageHistory.LastTimestamp + DiagnosticsHandlerHelper.DiagnosticsRefreshInterval < DateTime.UtcNow) { - this.systemUsageHistory = DiagnosticsHandlerHelper.Instance.GetDiagnosticsSystemHistory(); + this.systemUsageHistory = DiagnosticsHandlerHelper.GetInstance().GetDiagnosticsSystemHistory(); } #endif } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs index d31c95bfe8..7a54f490c0 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryReleaseTests.cs @@ -36,20 +36,15 @@ public override Task HttpHandlerRequestCallbackChecks(HttpR } [ClassInitialize] - public static new void ClassInitialize(TestContext context) + public static void ClassInit(TestContext context) { ClientTelemetryTestsBase.ClassInitialize(context); - - // It will go away in next PR - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, "true"); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "1"); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, "https://tools.cosmos.azure.com/api/clienttelemetry/trace"); } [ClassCleanup] - public static new void FinalCleanup() + public static void ClassCleanUp() { - ClientTelemetryTestsBase.FinalCleanup(); + ClientTelemetryTestsBase.ClassCleanup(); } [TestInitialize] diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs index 2ffc60f5f4..ffa3e6eda1 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTests.cs @@ -5,11 +5,13 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests { using Microsoft.Azure.Cosmos.Fluent; - using Microsoft.Azure.Cosmos.Telemetry; using System.Net.Http; using System.Net; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json; + using System.Text; + using Microsoft.Azure.Documents; /// /// In Emulator Mode, Run test against emulator and mock client telemetry service calls. @@ -21,10 +23,25 @@ public class ClientTelemetryTests : ClientTelemetryTestsBase { public override Task HttpHandlerRequestCallbackChecks(HttpRequestMessage request) { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NoContent)); // In Emulator test, send hardcoded response status code as there is no real communication happens with client telemetry service } + else if (request.RequestUri.AbsoluteUri.Contains(Paths.ClientConfigPathSegment)) + { + HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); + AccountClientConfiguration clientConfigProperties = new AccountClientConfiguration + { + ClientTelemetryConfiguration = new ClientTelemetryConfiguration + { + IsEnabled = true, + Endpoint = telemetryServiceEndpoint.AbsoluteUri + } + }; + string payload = JsonConvert.SerializeObject(clientConfigProperties); + result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); + return Task.FromResult(result); + } return null; } @@ -35,15 +52,15 @@ public override CosmosClientBuilder GetBuilder() } [ClassInitialize] - public static new void ClassInitialize(TestContext context) + public static void ClassInit(TestContext context) { ClientTelemetryTestsBase.ClassInitialize(context); } [ClassCleanup] - public static new void FinalCleanup() + public static void ClassCleanUp() { - ClientTelemetryTestsBase.FinalCleanup(); + ClientTelemetryTestsBase.ClassCleanup(); } [TestInitialize] diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs index da4b131cdb..30c239a036 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientTelemetryTestsBase.cs @@ -27,7 +27,8 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests public abstract class ClientTelemetryTestsBase : BaseCosmosClientHelper { - private static SystemUsageMonitor systemUsageMonitor; + protected static readonly Uri telemetryServiceEndpoint = new Uri("http://dummy.telemetry.service/api/url"); + private static readonly List preferredRegionList = new List { Regions.EastUS, @@ -53,13 +54,13 @@ public abstract class ClientTelemetryTestsBase : BaseCosmosClientHelper public static void ClassInitialize(TestContext _) { - Util.EnableClientTelemetryEnvironmentVariables(); - - SystemUsageMonitor oldSystemUsageMonitor = (SystemUsageMonitor)typeof(DiagnosticsHandlerHelper) - .GetField("systemUsageMonitor", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(DiagnosticsHandlerHelper.Instance); - oldSystemUsageMonitor.Stop(); + ClientTelemetryOptions.DefaultIntervalForTelemetryJob = TimeSpan.FromSeconds(1); + } - ClientTelemetryTestsBase.ResetSystemUsageMonitor(true); + public static void ClassCleanup() + { + //undone the changes done in ClassInitialize + ClientTelemetryOptions.DefaultIntervalForTelemetryJob = TimeSpan.FromMinutes(10); } public virtual void TestInitialize() @@ -70,7 +71,7 @@ public virtual void TestInitialize() { RequestCallBack = (request, cancellation) => { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); @@ -83,7 +84,7 @@ public virtual void TestInitialize() }, ResponseIntercepter = (response) => { - if (response.RequestMessage.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (response.RequestMessage != null && response.RequestMessage.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); } @@ -92,7 +93,7 @@ public virtual void TestInitialize() }, ExceptionIntercepter = (request, exception) => { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { this.isClientTelemetryAPICallFailed = true; } @@ -109,7 +110,7 @@ public virtual void TestInitialize() return Task.FromResult(result); } - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); @@ -123,7 +124,7 @@ public virtual void TestInitialize() }, ResponseIntercepter = (response) => { - if (response.RequestMessage.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (response.RequestMessage != null && response.RequestMessage.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode); } @@ -131,7 +132,7 @@ public virtual void TestInitialize() }, ExceptionIntercepter = (request, exception) => { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { this.isClientTelemetryAPICallFailed = true; } @@ -139,6 +140,7 @@ public virtual void TestInitialize() }; this.cosmosClientBuilder = this.GetBuilder() + .WithTelemetryEnabled() .WithApplicationPreferredRegions(ClientTelemetryTestsBase.preferredRegionList); } @@ -146,34 +148,6 @@ public virtual void TestInitialize() public abstract CosmosClientBuilder GetBuilder(); - private static void ResetSystemUsageMonitor(bool isTelemetryEnabled) - { - ClientTelemetryTestsBase.systemUsageMonitor?.Stop(); - - FieldInfo diagnosticsHandlerHelperInstance = typeof(DiagnosticsHandlerHelper) - .GetField("isTelemetryMonitoringEnabled", BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); - diagnosticsHandlerHelperInstance.SetValue(null, isTelemetryEnabled); - - List recorders = new List() - { - (SystemUsageRecorder)typeof(DiagnosticsHandlerHelper) - .GetField("diagnosticSystemUsageRecorder", - BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(DiagnosticsHandlerHelper.Instance) - }; - - if (isTelemetryEnabled) - { - recorders.Add( - (SystemUsageRecorder)typeof(DiagnosticsHandlerHelper) - .GetField("telemetrySystemUsageRecorder", - BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(DiagnosticsHandlerHelper.Instance)); - } - - ClientTelemetryTestsBase.systemUsageMonitor = SystemUsageMonitor.CreateAndStart(recorders); - } - public virtual async Task Cleanup() { FieldInfo isInitializedField = typeof(VmMetadataApiHandler).GetField("isInitialized", @@ -185,16 +159,12 @@ public virtual async Task Cleanup() BindingFlags.Static | BindingFlags.NonPublic); azMetadataField.SetValue(null, null); + await base.TestCleanup(); - Assert.IsFalse(this.isClientTelemetryAPICallFailed, $"Call to client telemetry service endpoint (i.e. {ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri}) failed"); + Assert.IsFalse(this.isClientTelemetryAPICallFailed, $"Call to client telemetry service endpoint (i.e. {telemetryServiceEndpoint}) failed"); } - public static void FinalCleanup() - { - ClientTelemetryTestsBase.ResetSystemUsageMonitor(false); - } - public virtual async Task PointSuccessOperationsTest(ConnectionMode mode, bool isAzureInstance) { Container container = await this.CreateClientAndContainer( @@ -414,8 +384,6 @@ await this.WaitAndAssert( public virtual async Task QueryOperationSinglePartitionTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); - Container container = await this.CreateClientAndContainer(mode); ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity("MyTestPkValue", "MyTestItemId"); @@ -471,7 +439,6 @@ await this.WaitAndAssert(expectedOperationCount: 4, public virtual async Task QueryMultiPageSinglePartitionOperationTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); Container container = await this.CreateClientAndContainer(mode: mode); ItemRequestOptions requestOptions = new ItemRequestOptions() @@ -532,8 +499,6 @@ await this.WaitAndAssert( public virtual async Task QueryOperationCrossPartitionTest(ConnectionMode mode) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "20"); - ContainerInternal itemsCore = (ContainerInternal)await this.CreateClientAndContainer( mode: mode, isLargeContainer: true); @@ -674,7 +639,7 @@ public virtual async Task CreateItemWithSubStatusCodeTest(ConnectionMode mode) httpHandler.RequestCallBack = (request, cancellation) => { - if (request.RequestUri.AbsoluteUri.Equals(ClientTelemetryOptions.GetClientTelemetryEndpoint().AbsoluteUri)) + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) { string jsonObject = request.Content.ReadAsStringAsync().GetAwaiter().GetResult(); @@ -696,11 +661,12 @@ public virtual async Task CreateItemWithSubStatusCodeTest(ConnectionMode mode) return Task.FromResult(result); } - return null; + return this.HttpHandlerRequestCallbackChecks(request); }; // Replacing originally initialized cosmos Builder with this one with new handler this.cosmosClientBuilder = this.cosmosClientBuilder + .WithTelemetryEnabled() .WithHttpClientFactory(() => new HttpClient(httpHandler)); Container container = await this.CreateClientAndContainer( diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SynchronizationContextTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SynchronizationContextTests.cs index a8ae6ce1a4..1c5676a46a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SynchronizationContextTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SynchronizationContextTests.cs @@ -6,9 +6,15 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests { using System; using System.Linq; + using System.Net; + using System.Net.Http; + using System.Text; using System.Threading; + using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Newtonsoft.Json; + using Microsoft.Azure.Documents; [TestClass] public class SynchronizationContextTests @@ -19,10 +25,29 @@ public class SynchronizationContextTests [Timeout(30000)] public void VerifySynchronizationContextDoesNotLock(bool withClientTelemetry) { - if (withClientTelemetry) + HttpClientHandlerHelper httpHandler = new HttpClientHandlerHelper { - Util.EnableClientTelemetryEnvironmentVariables(); - } + RequestCallBack = (request, cancellation) => + { + if (request.RequestUri.AbsoluteUri.Contains(Paths.ClientConfigPathSegment)) + { + HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); + AccountClientConfiguration clientConfigProperties = new AccountClientConfiguration + { + ClientTelemetryConfiguration = new ClientTelemetryConfiguration + { + IsEnabled = withClientTelemetry, + Endpoint = withClientTelemetry? "http://dummy.telemetry.endpoint/" : null + } + }; + string payload = JsonConvert.SerializeObject(clientConfigProperties); + result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); + return Task.FromResult(result); + } + + return null; + } + }; string databaseId = Guid.NewGuid().ToString(); SynchronizationContext prevContext = SynchronizationContext.Current; @@ -32,7 +57,10 @@ public void VerifySynchronizationContextDoesNotLock(bool withClientTelemetry) SynchronizationContext.SetSynchronizationContext(syncContext); syncContext.Post(_ => { - using (CosmosClient client = TestCommon.CreateCosmosClient()) + using (CosmosClient client = TestCommon.CreateCosmosClient( + customizeClientBuilder: (builder) => builder + .WithTelemetryEnabled() + .WithHttpClientFactory(() => new HttpClient(httpHandler)))) { Cosmos.Database database = client.CreateDatabaseAsync(databaseId).GetAwaiter().GetResult(); database = client.CreateDatabaseIfNotExistsAsync(databaseId).GetAwaiter().GetResult(); @@ -124,7 +152,7 @@ public void VerifySynchronizationContextDoesNotLock(bool withClientTelemetry) double cost = container.GetItemLinqQueryable( allowSynchronousQueryExecution: true).Select(x => x.cost).Sum(); - + ItemResponse deleteResponse = container.DeleteItemAsync(partitionKey: new Cosmos.PartitionKey(testItem.pk), id: testItem.id).ConfigureAwait(false).GetAwaiter().GetResult(); Assert.IsNotNull(deleteResponse); } @@ -138,11 +166,6 @@ public void VerifySynchronizationContextDoesNotLock(bool withClientTelemetry) { client.GetDatabase(databaseId).DeleteAsync().GetAwaiter().GetResult(); } - - if (withClientTelemetry) - { - Util.DisableClientTelemetryEnvironmentVariables(); - } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs index 9b78eb573c..aead33feef 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/HttpHandlerHelper.cs @@ -52,6 +52,8 @@ protected override async Task SendAsync(HttpRequestMessage throw; } this.ExceptionIntercepter.Invoke(request, ex); + + throw; // Anyway throw this exception } if (this.ResponseIntercepter != null) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/Util.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/Util.cs index 366cc68811..ea360ea285 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/Util.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/Util.cs @@ -522,20 +522,6 @@ internal static void LogRequestOptions(RequestOptions options, bool shouldLogOff options.OfferThroughput); } - internal static void EnableClientTelemetryEnvironmentVariables() - { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, "true"); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, "1"); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, "http://dummy.telemetry.endpoint/"); - } - - internal static void DisableClientTelemetryEnvironmentVariables() - { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, null); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetrySchedulingInSeconds, null); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, null); - } - private static TracerProvider OTelTracerProvider; private static CustomListener TestListener; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/HttpHandlerHelper.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/HttpHandlerHelper.cs new file mode 100644 index 0000000000..65fb15e012 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/HttpHandlerHelper.cs @@ -0,0 +1,67 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Performance.Tests +{ + using System; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + public class HttpClientHandlerHelper : DelegatingHandler + { + public HttpClientHandlerHelper() : base(new HttpClientHandler()) + { + } + + public Func> RequestCallBack { get; set; } + + public Func> ResponseIntercepter { get; set; } + + public Action ExceptionIntercepter { get; set; } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpResponseMessage httpResponse = null; + if (this.RequestCallBack != null) + { + Task response = this.RequestCallBack(request, cancellationToken); + if(response != null) + { + httpResponse = await response; + if (httpResponse != null) + { + if (this.ResponseIntercepter != null) + { + httpResponse = await this.ResponseIntercepter(httpResponse); + } + return httpResponse; + } + } + } + + try + { + httpResponse = await base.SendAsync(request, cancellationToken); + } + catch (Exception ex) { + + if (this.ExceptionIntercepter == null) + { + throw; + } + this.ExceptionIntercepter.Invoke(request, ex); + + throw; // Anyway throw this exception + } + + if (this.ResponseIntercepter != null) + { + httpResponse = await this.ResponseIntercepter(httpResponse); + } + + return httpResponse; + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs index 3403269950..3484abb3ae 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Mocks/MockDocumentClient.cs @@ -22,6 +22,9 @@ namespace Microsoft.Azure.Cosmos.Performance.Tests using Microsoft.Azure.Cosmos.Query.Core.QueryPlan; using Newtonsoft.Json; using Microsoft.Azure.Cosmos.Telemetry; + using System.Net.Http; + using System.Net; + using System.Text; internal class MockDocumentClient : DocumentClient, ICosmosAuthorizationTokenProvider { @@ -51,7 +54,7 @@ public static CosmosClient CreateMockCosmosClient( { policy = new ConnectionPolicy { - EnableClientTelemetry = isClientTelemetryEnabled.Value + EnableClientTelemetry = true // feature flag is always true }; } @@ -59,6 +62,43 @@ public static CosmosClient CreateMockCosmosClient( MockDocumentClient documentClient = new MockDocumentClient(policy); CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder("http://localhost", Convert.ToBase64String(Guid.NewGuid().ToByteArray())); cosmosClientBuilder.WithConnectionModeDirect(); + + Uri telemetryServiceEndpoint = new Uri("https://dummy.endpoint.com/"); + + if (isClientTelemetryEnabled.HasValue) + { + // mock external calls + HttpClientHandlerHelper httpHandler = new HttpClientHandlerHelper + { + RequestCallBack = (request, cancellation) => + { + if (request.RequestUri.AbsoluteUri.Equals(telemetryServiceEndpoint.AbsoluteUri)) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NoContent)); // In Emulator test, send hardcoded response status code as there is no real communication happens with client telemetry service + } + else if (request.RequestUri.AbsoluteUri.Contains(Paths.ClientConfigPathSegment)) + { + HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); + AccountClientConfiguration clientConfigProperties = new AccountClientConfiguration + { + ClientTelemetryConfiguration = new ClientTelemetryConfiguration + { + IsEnabled = isClientTelemetryEnabled.Value, + Endpoint = isClientTelemetryEnabled.Value?telemetryServiceEndpoint.AbsoluteUri: null + } + }; + string payload = JsonConvert.SerializeObject(clientConfigProperties); + result.Content = new StringContent(payload, Encoding.UTF8, "application/json"); + return Task.FromResult(result); + } + + return null; + } + }; + + cosmosClientBuilder.WithHttpClientFactory(() => new HttpClient(httpHandler)); + } + customizeClientBuilder?.Invoke(cosmosClientBuilder); if (useCustomSerializer) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Diagnostics/DiagnosticHandlerHelperTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Diagnostics/DiagnosticHandlerHelperTests.cs new file mode 100644 index 0000000000..23c813d826 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Diagnostics/DiagnosticHandlerHelperTests.cs @@ -0,0 +1,75 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Diagnostics +{ + using System; + using System.Reflection; + using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Handler; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class DiagnosticHandlerHelperTests + { + [ClassInitialize] + public static void Initialize(TestContext _) + { + DiagnosticHandlerHelperTests.ResetDiagnosticsHandlerHelper(); + } + + private static void ResetDiagnosticsHandlerHelper() + { + //Stop the job + DiagnosticsHandlerHelper helper = DiagnosticsHandlerHelper.GetInstance(); + MethodInfo iMethod = helper.GetType().GetMethod("StopSystemMonitor", BindingFlags.NonPublic | BindingFlags.Instance); + iMethod.Invoke(helper, new object[] { }); + + //Reset the instance woth original value + FieldInfo field = typeof(DiagnosticsHandlerHelper).GetField("Instance", + BindingFlags.Static | + BindingFlags.NonPublic); + field.SetValue(null, Activator.CreateInstance(typeof(DiagnosticsHandlerHelper), true)); + } + + [TestMethod] + public void SingletonTest() + { + DiagnosticsHandlerHelper diagnosticHandlerHelper1 = DiagnosticsHandlerHelper.GetInstance(); + DiagnosticsHandlerHelper diagnosticHandlerHelper2 = DiagnosticsHandlerHelper.GetInstance(); + + Assert.IsNotNull(diagnosticHandlerHelper1); + Assert.IsNotNull(diagnosticHandlerHelper2); + Assert.AreEqual(diagnosticHandlerHelper1, diagnosticHandlerHelper2, "Not Singleton"); + } + + [TestMethod] + public async Task RefreshTestAsync() + { + // Get default instance of DiagnosticsHandlerHelper with client telemetry disabled (default) + DiagnosticsHandlerHelper diagnosticHandlerHelper1 = DiagnosticsHandlerHelper.GetInstance(); + await Task.Delay(10000); // warm up + Assert.IsNotNull(diagnosticHandlerHelper1.GetDiagnosticsSystemHistory()); + int countBeforeRefresh = diagnosticHandlerHelper1.GetDiagnosticsSystemHistory().Values.Count; + + // Refresh instance of DiagnosticsHandlerHelper with client telemetry enabled + DiagnosticsHandlerHelper.Refresh(true); + DiagnosticsHandlerHelper diagnosticHandlerHelper2 = DiagnosticsHandlerHelper.GetInstance(); + int countAfterRefresh = diagnosticHandlerHelper1.GetDiagnosticsSystemHistory().Values.Count; + + Console.WriteLine(countBeforeRefresh + " " + countAfterRefresh); + Assert.IsTrue(countBeforeRefresh <= countAfterRefresh, "After Refresh count should be greater than or equal to before refresh count"); + + Assert.AreNotEqual(diagnosticHandlerHelper1, diagnosticHandlerHelper2); + + Assert.IsNotNull(diagnosticHandlerHelper2.GetDiagnosticsSystemHistory()); + Assert.IsNotNull(diagnosticHandlerHelper2.GetClientTelemetrySystemHistory()); + + // Refresh instance of DiagnosticsHandlerHelper with client telemetry disabled + DiagnosticsHandlerHelper.Refresh(false); + DiagnosticsHandlerHelper diagnosticHandlerHelper3 = DiagnosticsHandlerHelper.GetInstance(); + Assert.IsNotNull(diagnosticHandlerHelper3.GetDiagnosticsSystemHistory()); + Assert.IsNull(diagnosticHandlerHelper3.GetClientTelemetrySystemHistory()); + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/ClientTelemetryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/ClientTelemetryTests.cs index 9308d22806..6005ec3045 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/ClientTelemetryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/ClientTelemetryTests.cs @@ -26,13 +26,6 @@ namespace Microsoft.Azure.Cosmos.Tests.Telemetry [TestClass] public class ClientTelemetryTests { - [TestCleanup] - public void Cleanup() - { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, null); - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, null); - } - [TestMethod] public void CheckMetricsAggregationLogic() { @@ -126,7 +119,6 @@ public void CheckJsonSerializerContractWithPreferredRegions() [DataRow(150, 0, 0)] // When only operation info is there in payload public async Task CheckIfPayloadIsDividedCorrectlyAsync(int expectedOperationInfoSize, int expectedCacheRefreshInfoSize, int expectedRequestInfoSize) { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, "http://dummy.telemetry.endpoint/"); ClientTelemetryOptions.PayloadSizeThreshold = 1024 * 15; //15 Kb string data = File.ReadAllText("Telemetry/ClientTelemetryPayloadWithoutMetrics.json", Encoding.UTF8); @@ -245,7 +237,8 @@ await processor.ProcessAndSendAsync( clientTelemetryProperties, operationInfoSnapshot, cacheRefreshInfoSnapshot, - requestInfoList, + requestInfoList, + "http://dummy.telemetry.endpoint/", new CancellationTokenSource(ClientTelemetryOptions.ClientTelemetryProcessorTimeOut).Token); Assert.AreEqual(expectedOperationInfoSize, actualOperationInfoSize, "Operation Info is not correct"); @@ -256,8 +249,6 @@ await processor.ProcessAndSendAsync( [TestMethod] public async Task ClientTelmetryProcessor_should_timeout() { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEndpoint, "http://dummy.telemetry.endpoint/"); - string data = File.ReadAllText("Telemetry/ClientTelemetryPayloadWithoutMetrics.json", Encoding.UTF8); ClientTelemetryProperties clientTelemetryProperties = JsonConvert.DeserializeObject(data); @@ -344,6 +335,7 @@ await processor.ProcessAndSendAsync(clientTelemetryProperties, operationInfoSnapshot, cacheRefreshInfoSnapshot, default, + "http://dummy.telemetry.endpoint/", cts.Token); }); @@ -352,13 +344,5 @@ await Assert.ThrowsExceptionAsync(() => ClientTeleme processingTask: processorTask, timeout: TimeSpan.FromTicks(1))); } - - [TestMethod] - [ExpectedException(typeof(FormatException))] - public void CheckMisconfiguredTelemetry_should_fail() - { - Environment.SetEnvironmentVariable(ClientTelemetryOptions.EnvPropsClientTelemetryEnabled, "non-boolean"); - using CosmosClient client = MockCosmosUtil.CreateMockCosmosClient(); - } } }