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

ClientTelemetry : Adds logic to call client config in every 10 minutes #4071

Merged
33 changes: 21 additions & 12 deletions Microsoft.Azure.Cosmos/src/Handler/DiagnosticsHandlerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos.Handler
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Documents.Rntbd;
using Microsoft.Azure.Cosmos.Core.Trace;

Expand All @@ -21,16 +22,14 @@ internal class DiagnosticsHandlerHelper
private const string Telemetrykey = "telemetry";

public static readonly TimeSpan DiagnosticsRefreshInterval = TimeSpan.FromSeconds(10);
private static readonly SystemUsageRecorder DiagnosticSystemUsageRecorder = new SystemUsageRecorder(
private static readonly TimeSpan ClientTelemetryRefreshInterval = TimeSpan.FromSeconds(5);

// Need to reset it in Tests hence kept it non-readonly.
private static SystemUsageRecorder DiagnosticSystemUsageRecorder = new SystemUsageRecorder(
identifier: Diagnostickey,
historyLength: 6,
refreshInterval: DiagnosticsHandlerHelper.DiagnosticsRefreshInterval);

private static readonly TimeSpan ClientTelemetryRefreshInterval = TimeSpan.FromSeconds(5);
private static readonly SystemUsageRecorder TelemetrySystemUsageRecorder = new SystemUsageRecorder(
identifier: Telemetrykey,
historyLength: 120,
refreshInterval: DiagnosticsHandlerHelper.ClientTelemetryRefreshInterval);
private static SystemUsageRecorder TelemetrySystemUsageRecorder = null;

/// <summary>
/// Singleton to make sure only one instance of DiagnosticHandlerHelper is there.
Expand Down Expand Up @@ -62,13 +61,13 @@ public static void Refresh(bool isClientTelemetryEnabled)
{
if (isClientTelemetryEnabled != DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled)
{
DiagnosticsHandlerHelper.Instance.StopSystemMonitor();
DiagnosticsHandlerHelper tempInstance = DiagnosticsHandlerHelper.Instance;
kirankumarkolli marked this conversation as resolved.
Show resolved Hide resolved

// Update telemetry flag
DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled = isClientTelemetryEnabled;

// Create new instance, it will start a new system monitor job
DiagnosticsHandlerHelper.Instance = new DiagnosticsHandlerHelper();

// Stopping the monitor is a blocking call so we do it in a separate thread
_ = Task.Run(() => tempInstance.StopSystemMonitor());
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -102,8 +101,18 @@ private DiagnosticsHandlerHelper()

if (DiagnosticsHandlerHelper.isTelemetryMonitoringEnabled)
{
// re-initialize a fresh telemetry recorder when feature is switched on
DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder = new SystemUsageRecorder(
identifier: Telemetrykey,
historyLength: 120,
refreshInterval: DiagnosticsHandlerHelper.ClientTelemetryRefreshInterval);

recorders.Add(DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder);
}
else
{
DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder = null;
}

this.systemUsageMonitor = SystemUsageMonitor.CreateAndStart(recorders);

Expand Down Expand Up @@ -154,7 +163,7 @@ public SystemUsageHistory GetClientTelemetrySystemHistory()

try
{
return DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder.Data;
return DiagnosticsHandlerHelper.TelemetrySystemUsageRecorder?.Data;
}
catch (Exception ex)
{
Expand Down
72 changes: 58 additions & 14 deletions Microsoft.Azure.Cosmos/src/Telemetry/TelemetryToServiceHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ internal class TelemetryToServiceHelper : IDisposable
{
private ITelemetryCollector collector = new TelemetryCollectorNoOp();

internal static int DefaultBackgroundRefreshClientConfigTimeIntervalInMS = (int)TimeSpan.FromMinutes(10).TotalMilliseconds;
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
internal static TimeSpan DefaultBackgroundRefreshClientConfigTimeInterval
= TimeSpan.FromMinutes(10);

private readonly AuthorizationTokenProvider cosmosAuthorization;
private readonly CosmosHttpClient httpClient;
Expand Down Expand Up @@ -73,7 +74,12 @@ public static TelemetryToServiceHelper CreateAndInitializeClientConfigAndTelemet
}

TelemetryToServiceHelper helper = new TelemetryToServiceHelper(
clientId, connectionPolicy, cosmosAuthorization, httpClient, serviceEndpoint, globalEndpointManager, cancellationTokenSource);
clientId: clientId,
connectionPolicy: connectionPolicy,
cosmosAuthorization: cosmosAuthorization,
httpClient: httpClient, serviceEndpoint,
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
globalEndpointManager: globalEndpointManager,
cancellationTokenSource: cancellationTokenSource);

_ = helper.RetrieveConfigAndInitiateTelemetryAsync(); // Let it run in backgroud

Expand All @@ -86,15 +92,26 @@ private async Task RetrieveConfigAndInitiateTelemetryAsync()
try
{
Uri serviceEndpointWithPath = new Uri(this.serviceEnpoint + Paths.ClientConfigPathSegment);

TryCatch<AccountClientConfiguration> databaseAccountClientConfigs = await this.GetDatabaseAccountClientConfigAsync(this.cosmosAuthorization, this.httpClient, serviceEndpointWithPath);
if (databaseAccountClientConfigs.Succeeded)
{
this.InitializeClientTelemetry(databaseAccountClientConfigs.Result);
}
else if (!this.cancellationTokenSource.IsCancellationRequested)
while (!this.cancellationTokenSource.IsCancellationRequested)
{
DefaultTrace.TraceWarning($"Exception while calling client config " + databaseAccountClientConfigs.Exception.ToString());
TryCatch<AccountClientConfiguration> databaseAccountClientConfigs = await this.GetDatabaseAccountClientConfigAsync(
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
cosmosAuthorization: this.cosmosAuthorization,
httpClient: this.httpClient,
clientConfigEndpoint: serviceEndpointWithPath);

if (databaseAccountClientConfigs.Succeeded)
{
this.InitializeClientTelemetry(
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
clientConfig: databaseAccountClientConfigs.Result);
}
else if (!this.cancellationTokenSource.IsCancellationRequested)
{
DefaultTrace.TraceWarning($"Exception while calling client config " + databaseAccountClientConfigs.Exception.ToString());
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}

await Task.Delay(
delay: TelemetryToServiceHelper.DefaultBackgroundRefreshClientConfigTimeInterval,
cancellationToken: this.cancellationTokenSource.Token);
}
}
catch (Exception ex)
Expand Down Expand Up @@ -125,14 +142,28 @@ await cosmosAuthorization.AddAuthorizationHeaderAsync(
timeoutPolicy: HttpTimeoutPolicyControlPlaneRead.Instance,
clientSideRequestStatistics: null,
cancellationToken: default))
using (DocumentServiceResponse documentServiceResponse = await ClientExtensions.ParseResponseAsync(responseMessage))
{
return TryCatch<AccountClientConfiguration>.FromResult(CosmosResource.FromStream<AccountClientConfiguration>(documentServiceResponse));
// It means feature flag is off at gateway, then log the exception and retry after defined interval.
// If feature flag is OFF at gateway, SDK won't refresh the latest state of the flag.
if (responseMessage.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
throw new InvalidOperationException("Client Config API is not enabled at compute gateway.");
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}

using (DocumentServiceResponse documentServiceResponse = await ClientExtensions.ParseResponseAsync(responseMessage))
{
return TryCatch<AccountClientConfiguration>.FromResult(CosmosResource.FromStream<AccountClientConfiguration>(documentServiceResponse));
}
}
}
catch (ObjectDisposedException)
{
throw new OperationCanceledException($"Client is being disposed for {clientConfigEndpoint} at {DateTime.UtcNow}");
}
catch (Exception ex)
{
DefaultTrace.TraceWarning($"Exception while calling client config " + ex.StackTrace);
// Do not log if exception is due to client dispose.
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
DefaultTrace.TraceWarning($"Exception while calling client config " + ex);
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
return TryCatch<AccountClientConfiguration>.FromException(ex);
}
}
Expand All @@ -153,10 +184,16 @@ public bool IsClientTelemetryJobRunning()
/// </summary>
private void InitializeClientTelemetry(AccountClientConfiguration clientConfig)
{
// If state of the job is same as state of the flag, then no need to do anything.
if (clientConfig.IsClientTelemetryEnabled() == this.IsClientTelemetryJobRunning())
{
return;
}

DiagnosticsHandlerHelper.Refresh(clientConfig.IsClientTelemetryEnabled());

if (clientConfig.IsClientTelemetryEnabled())
{
{
try
{
this.clientTelemetry = ClientTelemetry.CreateAndStartBackgroundTelemetry(
Expand All @@ -180,6 +217,11 @@ private void InitializeClientTelemetry(AccountClientConfiguration clientConfig)
this.connectionPolicy.EnableClientTelemetry = false;
}
}
else
{
this.StopClientTelemetry();
DefaultTrace.TraceVerbose("Client Telemetry Disabled.");
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}
}

public void Dispose()
Expand All @@ -197,6 +239,8 @@ private void StopClientTelemetry()

this.clientTelemetry?.Dispose();
this.clientTelemetry = null;

DiagnosticsHandlerHelper.Refresh(isClientTelemetryEnabled: false);
}
}
}
Loading
Loading