[Internal] Benchmark tool: Adds Cosmos Benchmark Metrics (#3950)

* Adding metrics for Benchmark tool.

* Adding OpenTelemetry.

* Revert "Adding OpenTelemetry."

This reverts commit c7da088469.

* 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 c7da088469.

* 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 <chaava01@gmail.com>
Co-authored-by: David Chaava <v-dchaava@microsoft.com>
This commit is contained in:
Mikhail Lipin 2023-08-16 01:06:04 +03:00 коммит произвёл GitHub
Родитель e708ec9025
Коммит 946dd4a95f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 475 добавлений и 34 удалений

Просмотреть файл

@ -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;
/// <summary>
/// Represents Benchmark Configuration
/// </summary>
public class BenchmarkConfig
{
private static readonly string UserAgentSuffix = "cosmosdbdotnetbenchmark";
@ -123,6 +127,12 @@ namespace CosmosBenchmark
[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 @@ namespace CosmosBenchmark
{
foreach (Error e in errors)
{
Console.WriteLine(e.ToString());
Trace.TraceInformation(e.ToString());
}
}
Environment.Exit(errors.Count());
}
}
}
}

Просмотреть файл

@ -17,6 +17,7 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.0.0-beta.13" />
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="MathNet.Numerics" Version="4.15.0" />
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="*" />

Просмотреть файл

@ -6,6 +6,7 @@ namespace CosmosBenchmark
{
using System;
using System.Threading.Tasks;
using OpenTelemetry.Metrics;
internal interface IExecutionStrategy
{
@ -19,7 +20,7 @@ namespace CosmosBenchmark
BenchmarkConfig benchmarkConfig,
int serialExecutorConcurrency,
int serialExecutorIterationCount,
double warmupFraction);
double warmupFraction,
MetricsCollectorProvider metricsCollectorProvider);
}
}

Просмотреть файл

@ -17,6 +17,8 @@ namespace CosmosBenchmark
int iterationCount,
bool isWarmup,
bool traceFailures,
Action completionCallback);
Action completionCallback,
BenchmarkConfig benchmarkConfig,
MetricsCollectorProvider metricsCollectorProvider);
}
}

Просмотреть файл

@ -0,0 +1,36 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace CosmosBenchmark
{
using System;
/// <summary>
/// Represents the metrics collector.
/// </summary>
public interface IMetricsCollector
{
/// <summary>
/// Collects the number of successful operations.
/// </summary>
void CollectMetricsOnSuccess();
/// <summary>
/// Collects the number of failed operations.
/// </summary>
void CollectMetricsOnFailure();
/// <summary>
/// Records latency for success operations in milliseconda.
/// </summary>
/// <param name="milliseconds">The number of milliseconds to record.</param>
void RecordSuccessOpLatencyAndRps(TimeSpan timeSpan);
/// <summary>
/// Records latency for failed operations in milliseconda.
/// </summary>
/// <param name="milliseconds">The number of milliseconds to record.</param>
void RecordFailedOpLatencyAndRps(TimeSpan timeSpan);
}
}

Просмотреть файл

@ -0,0 +1,166 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace CosmosBenchmark
{
using System;
using System.Diagnostics.Metrics;
/// <summary>
/// Represents the metrics collector.
/// </summary>
internal class MetricsCollector : IMetricsCollector
{
/// <summary>
/// Represents the meter to collect metrics.
/// </summary>
private readonly Meter meter;
/// <summary>
/// Represents the histogram for operation latency.
/// </summary>
private readonly Histogram<double> operationLatencyHistogram;
/// <summary>
/// Represents the histogram for records per second metric.
/// </summary>
private readonly Histogram<double> rpsMetricNameHistogram;
/// <summary>
/// Represents the histogram for failed operation latency.
/// </summary>
private readonly Histogram<double> operationFailedLatencyHistogram;
/// <summary>
/// Represents the histogram failed operations for records per second metric.
/// </summary>
private readonly Histogram<double> rpsFailedMetricNameHistogram;
/// <summary>
/// Represents the success operation counter.
/// </summary>
private readonly Counter<long> successOperationCounter;
/// <summary>
/// Represents the failure operation counter.
/// </summary>
private readonly Counter<long> failureOperationCounter;
/// <summary>
/// Represents latency in milliseconds metric gauge.
/// </summary>
/// <remarks>Please do not remove this as it used when collecting metrics..</remarks>
private readonly ObservableGauge<double> latencyInMsMetricNameGauge;
/// <summary>
/// Represents records per second metric gauge.
/// </summary>
/// <remarks>Please do not remove this as it used when collecting metrics..</remarks>
private readonly ObservableGauge<double> rpsNameGauge;
/// <summary>
/// Represents latency in milliseconds metric gauge for failed operations.
/// </summary>
/// <remarks>Please do not remove this as it used when collecting metrics..</remarks>
private readonly ObservableGauge<double> latencyInMsFailedMetricNameGauge;
/// <summary>
/// Represents records per second metric gauge for failed operations.
/// </summary>
/// <remarks>Please do not remove this as it used when collecting metrics..</remarks>
private readonly ObservableGauge<double> rpsFailedNameGauge;
/// <summary>
/// Latency in milliseconds.
/// </summary>
private double latencyInMs;
/// <summary>
/// Records per second.
/// </summary>
private double rps;
/// <summary>
/// Latency in milliseconds.
/// </summary>
private double latencyFailedInMs;
/// <summary>
/// Records per second.
/// </summary>
private double rpsFailed;
/// <summary>
/// Initialize new instance of <see cref="MetricsCollector"/>.
/// </summary>
/// <param name="meter">OpenTelemetry meter.</param>
public MetricsCollector(Meter meter, string prefix)
{
this.meter = meter;
this.rpsMetricNameHistogram = meter.CreateHistogram<double>($"{prefix}OperationRpsHistogram");
this.operationLatencyHistogram = meter.CreateHistogram<double>($"{prefix}OperationLatencyInMsHistogram");
this.rpsFailedMetricNameHistogram = meter.CreateHistogram<double>($"{prefix}FailedOperationRpsHistogram");
this.operationFailedLatencyHistogram = meter.CreateHistogram<double>($"{prefix}FailedOperationLatencyInMsHistogram");
this.successOperationCounter = meter.CreateCounter<long>($"{prefix}OperationSuccess");
this.failureOperationCounter = meter.CreateCounter<long>($"{prefix}OperationFailure");
this.latencyInMsMetricNameGauge = this.meter.CreateObservableGauge($"{prefix}OperationLatencyInMs",
() => new Measurement<double>(this.latencyInMs));
this.rpsNameGauge = this.meter.CreateObservableGauge($"{prefix}OperationRps",
() => new Measurement<double>(this.rps));
this.latencyInMsFailedMetricNameGauge = this.meter.CreateObservableGauge($"{prefix}FailedOperationLatencyInMs",
() => new Measurement<double>(this.latencyInMs));
this.rpsFailedNameGauge = this.meter.CreateObservableGauge($"{prefix}FailedOperationRps",
() => new Measurement<double>(this.rps));
}
/// <summary>
/// Collects the number of successful operations.
/// </summary>
public void CollectMetricsOnSuccess()
{
this.successOperationCounter.Add(1);
}
/// <summary>
/// Collects the number of failed operations.
/// </summary>
public void CollectMetricsOnFailure()
{
this.failureOperationCounter.Add(1);
}
/// <summary>
/// Records success operation latency in milliseconds.
/// </summary>
/// <param name="milliseconds">The number of milliseconds to record.</param>
public void RecordSuccessOpLatencyAndRps(
TimeSpan timeSpan)
{
this.rps = 1000 / timeSpan.Milliseconds;
this.latencyInMs = timeSpan.Milliseconds;
this.rpsMetricNameHistogram.Record(this.rps);
this.operationLatencyHistogram.Record(this.latencyInMs);
}
/// <summary>
/// Records failed operation latency in milliseconds.
/// </summary>
/// <param name="milliseconds">The number of milliseconds to record.</param>
public void RecordFailedOpLatencyAndRps(
TimeSpan timeSpan)
{
this.rpsFailed = 1000 / timeSpan.Milliseconds;
this.latencyFailedInMs = timeSpan.Milliseconds;
this.rpsFailedMetricNameHistogram.Record(this.rpsFailed);
this.operationFailedLatencyHistogram.Record(this.latencyFailedInMs);
}
}
}

Просмотреть файл

@ -0,0 +1,68 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace CosmosBenchmark
{
using System;
using System.Diagnostics.Metrics;
using OpenTelemetry.Metrics;
/// <summary>
/// Represents the metrics collector provider.
/// </summary>
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);
}
/// <summary>
/// Gets the metric collector.
/// </summary>
/// <param name="benchmarkOperation">Benchmark operation.</param>
/// <param name="config">Benchmark configuration.</param>
/// <returns>Metrics collector.</returns>
/// <exception cref="NotSupportedException">Thrown if provided benchmark operation is not covered supported to collect metrics.</exception>
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."),
};
}
}
}

Просмотреть файл

@ -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 @@ namespace CosmosBenchmark
BenchmarkConfig benchmarkConfig,
int serialExecutorConcurrency,
int serialExecutorIterationCount,
double warmupFraction)
double warmupFraction,
MetricsCollectorProvider metricsCollectorProvider)
{
IExecutor warmupExecutor = new SerialOperationExecutor(
executorId: "Warmup",
@ -37,7 +39,9 @@ namespace CosmosBenchmark
(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 @@ namespace CosmosBenchmark
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 @@ namespace CosmosBenchmark
}
}
}
}
}

Просмотреть файл

@ -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 @@ namespace CosmosBenchmark
}
public int SuccessOperationCount { get; private set; }
public int FailedOperationCount { get; private set; }
public double TotalRuCharges { get; private set; }
@ -34,27 +37,38 @@ namespace CosmosBenchmark
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 @@ namespace CosmosBenchmark
{
if (traceFailures)
{
Console.WriteLine(ex.ToString());
Trace.TraceInformation(ex.ToString());
}
telemetrySpan.MarkFailed();
metricsCollector.CollectMetricsOnFailure();
// failure case
this.FailedOperationCount++;
@ -96,10 +113,14 @@ namespace CosmosBenchmark
Trace.TraceInformation($"Executor {this.executorId} completed");
}
catch (Exception e)
{
Trace.TraceInformation($"Error: {e.Message}");
}
finally
{
completionCallback();
}
}
}
}
}

Просмотреть файл

@ -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 @@ namespace CosmosBenchmark
private Stopwatch stopwatch;
private Func<OperationResult> lazyOperationResult;
private Action<TimeSpan> recordFailedOpLatencyAction;
private Action<TimeSpan> recordSuccessOpLatencyAction;
private bool disableTelemetry;
private bool isFailed = false;
private BenchmarkConfig benchmarkConfig;
public static IDisposable StartNew(
public static ITelemetrySpan StartNew(
BenchmarkConfig benchmarkConfig,
Func<OperationResult> lazyOperationResult,
bool disableTelemetry)
bool disableTelemetry,
Action<TimeSpan> recordSuccessOpLatencyAction,
Action<TimeSpan> recordFailedOpLatencyAction)
{
if (disableTelemetry || !TelemetrySpan.IncludePercentile)
{
@ -31,12 +39,17 @@ namespace CosmosBenchmark
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 @@ namespace CosmosBenchmark
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 @@ namespace CosmosBenchmark
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 @@ namespace CosmosBenchmark
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();
}
}
}

Просмотреть файл

@ -0,0 +1,48 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace CosmosBenchmark
{
using System;
/// <summary>
/// Represents the metric collection window (time span while accumulating and granulating the data)
/// </summary>
internal class MetricCollectionWindow
{
/// <summary>
/// The timestamp when window span is started.
/// </summary>
public DateTime Started { get; private set; }
/// <summary>
/// The timestamp until which the current window span is not elapsed.
/// </summary>
public DateTime ValidTill { get; private set; }
/// <summary>
/// Creates the instance of <see cref="MetricCollectionWindow"/>.
/// </summary>
/// <param name="config">Cosmos Benchmark configuration.</param>
public MetricCollectionWindow(BenchmarkConfig config)
{
this.Reset(config);
}
/// <summary>
/// Indicates whether the current window is valid.
/// </summary>
public bool IsValid => DateTime.UtcNow > this.ValidTill;
/// <summary>
/// Resets the started timestamp and valid till timespan.
/// </summary>
/// <param name="config"></param>
public void Reset(BenchmarkConfig config)
{
this.Started = DateTime.UtcNow;
this.ValidTill = this.Started.AddSeconds(config.MetricsReportingIntervalInSec);
}
}
}

Просмотреть файл

@ -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;
/// <summary>
/// This sample demonstrates how to achieve high performance writes using Azure Comsos DB.
@ -31,8 +36,12 @@ namespace CosmosBenchmark
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 @@ namespace CosmosBenchmark
Program program = new Program();
RunSummary runSummary = await program.ExecuteAsync(config);
RunSummary runSummary = await program.ExecuteAsync(config, metricsCollectorProvider);
}
finally
{
@ -58,6 +67,37 @@ namespace CosmosBenchmark
}
}
/// <summary>
/// Create a MeterProvider. If the App Insights connection string is not set, do not create an AppInsights Exporter.
/// </summary>
/// <returns></returns>
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<AzureMonitorExporterOptions>(
(options) => options.ConnectionString = config.AppInsightsConnectionString))
.AddMeter("CosmosBenchmarkInsertOperationMeter")
.AddMeter("CosmosBenchmarkQueryOperationMeter")
.AddMeter("CosmosBenchmarkReadOperationMeter")
.Build();
}
/// <summary>
/// Adds Azure VM information to run summary.
/// </summary>
/// <returns></returns>
private static async Task AddAzureInfoToRunSummary()
{
using HttpClient httpClient = new HttpClient();
@ -81,11 +121,13 @@ namespace CosmosBenchmark
}
}
/// <summary>
/// Executing benchmarks for V2/V3 cosmosdb SDK.
/// </summary>
/// <returns>a Task object.</returns>
private async Task<RunSummary> ExecuteAsync(BenchmarkConfig config)
private async Task<RunSummary> ExecuteAsync(BenchmarkConfig config,
MetricsCollectorProvider metricsCollectorProvider)
{
// V3 SDK client initialization
using (CosmosClient cosmosClient = config.CreateCosmosClient(config.Key))
@ -137,7 +179,7 @@ namespace CosmosBenchmark
}
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 @@ namespace CosmosBenchmark
}
runSummary.ConsistencyLevel = consistencyLevel;
if (config.PublishResults)
{
runSummary.Diagnostics = CosmosDiagnosticsLogger.GetDiagnostics();
await this.PublishResults(
config,
runSummary,
config,
runSummary,
cosmosClient);
}
@ -169,8 +210,8 @@ namespace CosmosBenchmark
}
private async Task PublishResults(
BenchmarkConfig config,
RunSummary runSummary,
BenchmarkConfig config,
RunSummary runSummary,
CosmosClient benchmarkClient)
{
if (string.IsNullOrEmpty(config.ResultsEndpoint))
@ -266,8 +307,8 @@ namespace CosmosBenchmark
{
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 @@ namespace CosmosBenchmark
traceSource.Listeners.Clear();
}
}
}
}