Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

UserAgent: Adds flag to user agent to show if region failover is configured #2487

Merged
merged 2 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -828,21 +828,42 @@ internal void SetUserAgentFeatures(UserAgentContainer userAgent)
features |= CosmosClientOptionsFeatures.HttpClientFactory;
}

string featureString = null;
if (features != CosmosClientOptionsFeatures.NoFeatures)
{
string featureString = Convert.ToString((int)features, 2).PadLeft(8, '0');
if (!string.IsNullOrEmpty(featureString))
{
userAgent.SetFeatures(featureString);
}
featureString = Convert.ToString((int)features, 2).PadLeft(8, '0');
}

string regionConfiguration = this.GetRegionConfiguration();
userAgent.SetFeatures(featureString, regionConfiguration);

if (!string.IsNullOrEmpty(this.ApplicationName))
{
userAgent.Suffix = this.ApplicationName;
}
}

/// <summary>
/// This generates a key that added to the user agent to make it
/// possible to determine if the SDK has region failover enabled.
/// </summary>
/// <returns>Format Reg-{D (Disabled discovery)}-S(application region)|L(List of preferred regions)|N(None, user did not configure it)</returns>
private string GetRegionConfiguration()
{
string regionConfig = this.LimitToEndpoint ? "D" : string.Empty;
if (!string.IsNullOrEmpty(this.ApplicationRegion))
{
return regionConfig + "S";
}

if (this.ApplicationPreferredRegions != null)
{
return regionConfig + "L";
}

return regionConfig + "N";
}

/// <summary>
/// Serialize the current configuration into a JSON string
/// </summary>
Expand Down
17 changes: 14 additions & 3 deletions Microsoft.Azure.Cosmos/src/UserAgentContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ internal override string BaseUserAgent
}
}

internal void SetFeatures(string features)
internal void SetFeatures(
string features,
string regionConfiguration)
{
// Regenerate base user agent to account for features
this.cosmosBaseUserAgent = this.CreateBaseUserAgentString(features);
this.cosmosBaseUserAgent = this.CreateBaseUserAgentString(
features: features,
regionConfiguration: regionConfiguration);
this.Suffix = string.Empty;
}

Expand All @@ -55,7 +59,9 @@ protected virtual void GetEnvironmentInformation(
runtimeFramework = environmentInformation.RuntimeFramework;
}

private string CreateBaseUserAgentString(string features = null)
private string CreateBaseUserAgentString(
string features = null,
string regionConfiguration = null)
{
this.GetEnvironmentInformation(
out string clientVersion,
Expand All @@ -74,6 +80,11 @@ private string CreateBaseUserAgentString(string features = null)
// Do not change the cosmos-netstandard-sdk as it is required for reporting
string baseUserAgent = $"cosmos-netstandard-sdk/{clientVersion}" + Regex.Replace($"|{directVersion}|{clientId}|{processArchitecture}|{operatingSystem}|{runtimeFramework}|", @"[^0-9a-zA-Z\.\|\-]+", " ");

if (!string.IsNullOrEmpty(regionConfiguration))
{
baseUserAgent += $"{regionConfiguration}|";
}

if (!string.IsNullOrEmpty(features))
{
baseUserAgent += $"F {features}|";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,105 @@ public void VerifyUserAgentContent()
Assert.AreEqual(envInfo.RuntimeFramework, values[5]);
}

[TestMethod]
public async Task VerifyUserAgentWithRegionConfiguration()
{
string databaseName = Guid.NewGuid().ToString();
string containerName = Guid.NewGuid().ToString();

{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions();

// N - None. The user did not configure anything
string userAgentContentToValidate = "|N|";
await this.ValidateUserAgentStringAsync(
cosmosClientOptions,
userAgentContentToValidate,
databaseName,
containerName);
}

{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions
{
LimitToEndpoint = true
};
// D - Disabled endpoint discovery, N - None. The user did not configure anything
string userAgentContentToValidate = "|DN|";
await this.ValidateUserAgentStringAsync(
cosmosClientOptions,
userAgentContentToValidate,
databaseName,
containerName);
}

{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions
{
ApplicationRegion = Regions.EastUS
};

// S - Single application region is set
string userAgentContentToValidate = "|S|";
await this.ValidateUserAgentStringAsync(
cosmosClientOptions,
userAgentContentToValidate,
databaseName,
containerName);
}

{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions
{
LimitToEndpoint = false,
ApplicationRegion = null,
ApplicationPreferredRegions = new List<string>()
{
Regions.EastUS,
Regions.WestUS
}
};

// L - List of region is set
string userAgentContentToValidate = "|L|";
await this.ValidateUserAgentStringAsync(
cosmosClientOptions,
userAgentContentToValidate,
databaseName,
containerName);
}

using (CosmosClient client = TestCommon.CreateCosmosClient())
{
await client.GetDatabase(databaseName).DeleteStreamAsync();
}
}

private async Task ValidateUserAgentStringAsync(
CosmosClientOptions cosmosClientOptions,
string userAgentContentToValidate,
string databaseName,
string containerName)
{
HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper()
{
RequestCallBack = (request, cancellationToken) =>
{
string userAgent = request.Headers.UserAgent.ToString();
Assert.IsTrue(userAgent.Contains(userAgentContentToValidate));
return null;
}
};

cosmosClientOptions.HttpClientFactory = () => new HttpClient(httpClientHandlerHelper);

using (CosmosClient client = TestCommon.CreateCosmosClient(cosmosClientOptions))
{
Cosmos.Database db = await client.CreateDatabaseIfNotExistsAsync(databaseName);
await db.CreateContainerIfNotExistsAsync(containerName, "/pk");
}
}

[TestMethod]
[DataRow(true, true)]
[DataRow(true, false)]
Expand Down Expand Up @@ -174,7 +273,7 @@ internal override ConnectionPolicy GetConnectionPolicy()
{
ConnectionPolicy connectionPolicy = base.GetConnectionPolicy();
MacOsUserAgentContainer userAgent = new MacOsUserAgentContainer();

this.SetUserAgentFeatures(userAgent);

connectionPolicy.UserAgentContainer = userAgent;
Expand Down