This commit is contained in:
Zeeshaun
2026-03-13 09:11:35 -05:00
parent f004c8205a
commit c2d6bda458
197 changed files with 14970 additions and 0 deletions
@@ -0,0 +1,11 @@
namespace LCU.API.Internal.Document.PerformanceTest.API;
public static class ApiFactory
{
public static IDocumentApi Api => Environment.Type switch
{
EnvironmentType.Local => new DirectInMemoryDocumentApi(),
EnvironmentType.Azure => new DirectInMemoryDocumentApi(),
_ => throw new NotImplementedException()
};
}
@@ -0,0 +1,49 @@
using LCU.API.Internal.Document.Configurations;
using LCU.API.Internal.Document.Controllers;
using LCU.API.Internal.Document.PerformanceTest.DB;
using LCU.API.Internal.DocumentLib.Configurations;
using LCU.API.Internal.DocumentLib.DataAccess;
using LCU.Library.Internal.DocumentModels.Requests.Search;
using LCU.Library.Internal.DocumentModels.Responses;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;
namespace LCU.API.Internal.Document.PerformanceTest.API;
public class DirectInMemoryDocumentApi : IDocumentApi
{
private IServiceProvider? _services;
public Task SpinUp(DatabaseConfiguration databaseConfiguration)
{
if (_services is not null) return Task.CompletedTask;
var services = new ServiceCollection();
ServiceRegistrations.AddDocumentServices(services);
ServiceRegistrations.AddDocumentDataAccesses(services);
ServiceRegistrations.AddDocumentRepositories(services);
services.AddTransient<DocumentController>();
services.TryAddSingleton(databaseConfiguration);
services.AddSingleton<IBlobStorageClient, NullBlobStorageClient>();
services.AddSingleton<ApiConfiguration>();
services.AddSingleton<NotificationApiConfiguration>();
_services = services.BuildServiceProvider();
return Task.CompletedTask;
}
public async Task<IList<DocumentInformationResponse>> GetDocumentInformationByFilter(DocumentInformationFilter filter)
{
var controller = _services?.GetRequiredService<DocumentController>() ?? throw new InvalidOperationException();
var response = await controller.PostDocumentInformationByFilter(new DocumentInformationFilteredSearch{ Filter = filter });
return (response as OkObjectResult)?.Value as IList<DocumentInformationResponse> ?? throw new InvalidOperationException();
}
public Task TearDown()
{
return Task.CompletedTask;
}
}
@@ -0,0 +1,12 @@
using LCU.API.Internal.DocumentLib.Configurations;
using LCU.Library.Internal.DocumentModels.Requests.Search;
using LCU.Library.Internal.DocumentModels.Responses;
namespace LCU.API.Internal.Document.PerformanceTest.API;
public interface IDocumentApi
{
Task SpinUp(DatabaseConfiguration databaseConfiguration);
Task<IList<DocumentInformationResponse>> GetDocumentInformationByFilter(DocumentInformationFilter filter);
Task TearDown();
}
@@ -0,0 +1,99 @@
using Azure;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.CosmosDB;
using Azure.ResourceManager.CosmosDB.Models;
using Azure.ResourceManager.Resources;
using MongoDB.Driver;
namespace LCU.API.Internal.Document.PerformanceTest.DB;
public class AzureCosmosDb : IDatabase
{
private readonly string _performanceTestingResourceName = "Perf_MongoDb";
public async Task<IMongoClient> SpinUp()
{
Console.WriteLine("Currently in Azure Spin Up.");
//todo test this - cd: I'm not connected to the landmark subscription in the portal, only AD
var azureClient = new ArmClient(new DefaultAzureCredential());
var subscription = await azureClient.GetDefaultSubscriptionAsync();
var resourceGroup = (
await subscription
.GetResourceGroups()
.CreateOrUpdateAsync
(
WaitUntil.Completed,
_performanceTestingResourceName,
new ResourceGroupData(AzureLocation.CentralUS)
)
)
.Value;
var cosmosAccount = (
await resourceGroup
.GetCosmosDBAccounts()
.CreateOrUpdateAsync
(
WaitUntil.Completed,
_performanceTestingResourceName.ToLower().Replace("_",""),
new CosmosDBAccountCreateOrUpdateContent
(
AzureLocation.CentralUS,
new CosmosDBAccountLocation[] {
new CosmosDBAccountLocation {
LocationName = AzureLocation.CentralUS,
FailoverPriority = 0,
IsZoneRedundant = false
},
new CosmosDBAccountLocation {
LocationName = AzureLocation.EastUS2,
FailoverPriority = 1,
IsZoneRedundant = false
}
}
)
{ Kind = "MongoDB"}
)
)
.Value;
var mongoDb = cosmosAccount.GetMongoDBDatabases();
await mongoDb
.CreateOrUpdateAsync
(
WaitUntil.Completed,
_performanceTestingResourceName,
new MongoDBDatabaseCreateOrUpdateContent
(
AzureLocation.CentralUS,
new MongoDBDatabaseResourceInfo(_performanceTestingResourceName)
)
);
//todo test to ensure that there's only one connection string or that it can be determined which is the one we need for mongo
var allConnections = cosmosAccount
.GetConnectionStrings()
.AsPages()
.ToArray()
.SelectMany(p => p.Values)
.ToArray();
if (allConnections.Length == 0) throw new Exception("No Connection Strings Found.");
_connectionString = cosmosAccount.GetConnectionStrings().AsPages().ToArray()[0].Values[0].ConnectionString;
return new MongoClient(ConnectionString);
}
public async Task TearDown()
{
var azureClient = new ArmClient(new DefaultAzureCredential());
var subscription = await azureClient.GetDefaultSubscriptionAsync();
var resourceGroup = (await subscription.GetResourceGroupAsync(_performanceTestingResourceName)).Value;
await resourceGroup.DeleteAsync(WaitUntil.Completed);
}
public string ConnectionString => _connectionString ?? throw new InvalidOperationException("spin up the database first");
private string? _connectionString;
}
@@ -0,0 +1,11 @@
namespace LCU.API.Internal.Document.PerformanceTest.DB;
public static class DatabaseFactory
{
public static IDatabase Database => Environment.Type switch
{
EnvironmentType.Local => new LocalMongoDatabase(),
EnvironmentType.Azure => new AzureCosmosDb(),
_ => throw new NotImplementedException()
};
}
@@ -0,0 +1,10 @@
using MongoDB.Driver;
namespace LCU.API.Internal.Document.PerformanceTest.DB;
public interface IDatabase
{
Task<IMongoClient> SpinUp();
Task TearDown();
string ConnectionString { get; }
}
@@ -0,0 +1,26 @@
using Mongo2Go;
using MongoDB.Driver;
namespace LCU.API.Internal.Document.PerformanceTest.DB;
public class LocalMongoDatabase : IDatabase
{
private MongoDbRunner? _mongoDb;
public Task<IMongoClient> SpinUp()
{
_mongoDb ??= MongoDbRunner.Start();
return Task.FromResult<IMongoClient>(new MongoClient(ConnectionString));
}
public Task TearDown()
{
_mongoDb?.Dispose();
_mongoDb = null;
return Task.CompletedTask;
}
public string ConnectionString => _mongoDb?.ConnectionString ?? throw new InvalidOperationException("spin up the database first");
}
@@ -0,0 +1,9 @@
using Azure.Storage.Blobs;
using LCU.API.Internal.DocumentLib.DataAccess;
namespace LCU.API.Internal.Document.PerformanceTest.DB;
public class NullBlobStorageClient : IBlobStorageClient
{
public BlobContainerClient GetClient() => null!;
}
@@ -0,0 +1,12 @@
namespace LCU.API.Internal.Document.PerformanceTest;
public enum EnvironmentType
{
Local,
Azure
}
public static class Environment
{
public static EnvironmentType Type { get; set; } = EnvironmentType.Local;
}
@@ -0,0 +1,120 @@
using BenchmarkDotNet.Attributes;
using LCU.API.Internal.Document.PerformanceTest.API;
using LCU.API.Internal.Document.PerformanceTest.DB;
using LCU.API.Internal.DocumentLib.Configurations;
using LCU.API.Internal.DocumentLib.Containers;
using LCU.Library.Internal.DocumentModels.Requests.Search;
using MongoDB.Driver;
namespace LCU.API.Internal.Document.PerformanceTest;
[RPlotExporter]
public class GetDocumentInformationByFilterLoadTest
{
static GetDocumentInformationByFilterLoadTest()
{
Environment.Type = EnvironmentType.Local;
}
//[Params(1, 10, 100, 1000, 10000, 100000, 250000, 500000, 750000, 1000000, 2000000)]
[Params(1, 10, 100, 1000, 10000, 100000, 250000, 500000, 750000, 1000000)]
public int DocumentCount { get; set; }
private DocumentInformation _document = null!;
private readonly DatabaseConfiguration _config;
private readonly IDatabase _database = DatabaseFactory.Database;
private readonly IDocumentApi _api = ApiFactory.Api;
private IMongoClient _client = null!;
public GetDocumentInformationByFilterLoadTest()
{
_config = new DatabaseConfiguration
{
DatabaseName = $"{DocumentCount}Documents",
DocumentContainerName = "Documents",
AuditContainerName = "Audits"
};
}
[GlobalSetup]
public void GlobalSetup()
{
_client = _database.SpinUp().Result;
_config.ConnectionString = _database.ConnectionString;
var db = _client.GetDatabase(_config.DatabaseName);
db.CreateCollection(_config.DocumentContainerName);
db.CreateCollection(_config.AuditContainerName);
var documentCollection = db.GetCollection<DocumentInformation>(_config.DocumentContainerName);
Task.WaitAll
(
_api.SpinUp(_config),
documentCollection.InsertManyAsync(Enumerable.Repeat(() => DocumentFactory(), DocumentCount).Select(f => f()))
);
}
[IterationSetup]
public void Setup()
{
_document = DocumentFactory();
_client.GetDatabase(_config.DatabaseName)
.GetCollection<DocumentInformation>(_config.DocumentContainerName)
.InsertOne(_document);
}
[Benchmark]
public void GetDocumentInformationByFilterRun()
{
var oneThirdAttributes = _document.Attributes.Count / 3;
var filter = new DocumentInformationFilter
{
AttributesContains = _document.Attributes.ToArray()[..oneThirdAttributes].ToDictionary(p => p.Key, p => p.Value),
AttributesContainsAny = _document.Attributes.ToArray()[oneThirdAttributes..^oneThirdAttributes].ToDictionary(p => p.Key, p => p.Value),
AttributesEquals = _document.Attributes.ToArray()[^oneThirdAttributes..].ToDictionary(p => p.Key, p => p.Value),
DataSource = _document.DataSource,
Name = _document.Name,
Type = _document.Type,
Source = _document.Source,
ScanStatus = _document.ScanStatus,
};
_api.GetDocumentInformationByFilter(filter).Wait();
}
[GlobalCleanup]
public void GlobalCleanup()
{
_client.DropDatabase(_config.DatabaseName);
var dbDestruction = _database.TearDown();
var apiDestruction = _api.TearDown();
Task.WaitAll(dbDestruction, apiDestruction);
}
private static readonly Random Random = new Random(DateTime.Now.Millisecond);
private static DocumentInformation DocumentFactory() => new()
{
Name = Guid.NewGuid().ToString(),
Type = Random.Next().ToString(),
Source = Random.Next().ToString(),
DataSource = Random.Next().ToString(),
FileSize = Random.Next(),
ScanStatus = Random.Next() % 2 == 0,
UploadDate = DateTime.Now.AddDays(-Random.Next(1, 10)),
LastModifiedDate = DateTime.Now.AddDays(-Random.Next(1, 10)),
CreatedDate = DateTime.Now.AddDays(-Random.Next(1, 10)),
Attributes = Enumerable
.Repeat
(
() => new KeyValuePair<string, string>(Guid.NewGuid().ToString(), Random.Next().ToString()),
Random.Next(10, 100)
)
.Select(f => f())
.ToDictionary(p => p.Key, p => p.Value)
};
}
@@ -0,0 +1,130 @@
using System.Configuration;
using Azure.Identity;
using BenchmarkDotNet.Attributes;
using LCU.API.Internal.Document.PerformanceTest.API;
using LCU.API.Internal.DocumentLib.Configurations;
using LCU.API.Internal.DocumentLib.Containers;
using LCU.Library.Internal.DocumentModels.Requests.Search;
using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
namespace LCU.API.Internal.Document.PerformanceTest;
public enum Indexing
{
Default,
FieldSpecific,
Wildcard
}
//todo it would be great to get the RU cost output
[RPlotExporter]
[ShortRunJob]
public class IndexingStrategyPerformanceTest
{
[Params(Indexing.Default, Indexing.FieldSpecific, Indexing.Wildcard)]
public Indexing Strategy { get; set; }
private readonly IDocumentApi _api = ApiFactory.Api;
private readonly DatabaseConfiguration _config = new();
public IndexingStrategyPerformanceTest()
{
var configBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json");
var baseConfig = configBuilder.Build();
var clientSecretCredentials = new ClientSecretCredential
(
baseConfig["TenantId"],
baseConfig["ClientId"],
baseConfig["ClientSecret"]
);
var vaultLocation = new Uri($"https://{baseConfig["AzureKeyVaultUrl"]}.vault.azure.net/");
configBuilder
.AddAzureKeyVault(vaultLocation, clientSecretCredentials)
.Build()
.Bind("MongoDatabase", _config);
}
[GlobalSetup]
public void GlobalSetup()
{
switch (Strategy)
{
case Indexing.Default:
break;
case Indexing.FieldSpecific:
new MongoClient(_config.ConnectionString)
.GetDatabase(_config.DatabaseName)
.GetCollection<DocumentInformation>(_config.DocumentContainerName)
.Indexes
.CreateOne
(
new CreateIndexModel<DocumentInformation>
(
Builders<DocumentInformation>.IndexKeys.Ascending(d => d.Name),
new CreateIndexOptions { Name = nameof(DocumentInformation.Name) }
)
);
break;
case Indexing.Wildcard:
new MongoClient(_config.ConnectionString)
.GetDatabase(_config.DatabaseName)
.GetCollection<DocumentInformation>(_config.DocumentContainerName)
.Indexes
.CreateOne
(
new CreateIndexModel<DocumentInformation>
(
Builders<DocumentInformation>.IndexKeys.Wildcard(),
new CreateIndexOptions { Name = "wildcard" }
)
);
break;
default:
throw new ArgumentOutOfRangeException();
}
_api.SpinUp(_config).Wait();
}
[Benchmark]
//todo report the RU cost in the benchmark
public async Task DocumentSearch_ByName()
{
var filter = new DocumentInformationFilter
{
Name = "5498-SA"
};
await _api.GetDocumentInformationByFilter(filter);
}
[GlobalCleanup]
public void GlobalCleanup()
{
switch (Strategy)
{
case Indexing.Default:
break;
case Indexing.FieldSpecific:
new MongoClient(_config.ConnectionString)
.GetDatabase(_config.DatabaseName)
.GetCollection<DocumentInformation>(_config.DocumentContainerName)
.Indexes
.DropOne(nameof(DocumentInformation.Name));
break;
case Indexing.Wildcard:
new MongoClient(_config.ConnectionString)
.GetDatabase(_config.DatabaseName)
.GetCollection<DocumentInformation>(_config.DocumentContainerName)
.Indexes
.DropOne("wildcard");
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.ResourceManager" Version="1.3.1" />
<PackageReference Include="Azure.ResourceManager.CosmosDB" Version="1.0.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="Mongo2Go" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
<Content Include="..\LCU.API.Internal.Document\appsettings.json" Link="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LCU.API.Internal.DocumentLib\LCU.API.Internal.DocumentLib.csproj" />
<ProjectReference Include="..\LCU.API.Internal.Document\LCU.API.Internal.Document.csproj" />
<ProjectReference Include="..\LCU.Library.Internal.DocumentModels\LCU.Library.Internal.DocumentModels.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,5 @@
using BenchmarkDotNet.Running;
using LCU.API.Internal.Document.PerformanceTest;
//BenchmarkRunner.Run<GetDocumentInformationByFilterLoadTest>();
BenchmarkRunner.Run<IndexingStrategyPerformanceTest>();
@@ -0,0 +1,122 @@
# Running Benchmarks
`dotnet run -c Release`
## past results
### 2022.09.09 - local mongodb - "old" regex matching
note: default mongodb indexing is not the same as CosmosDB Mongo default indexing
``` ini
BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19042.1826/20H2/October2020Update)
11th Gen Intel Core i5-1145G7 2.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.400
[Host] : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
Job-HPFGMU : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
InvocationCount=1 UnrollFactor=1
```
| Method | DocumentCount | Mean | Error | StdDev |
|---------------------------------- |-------------- |--------------:|--------------:|--------------:|
| **GetDocumentInformationByFilterRun** | **1** | **3.756 ms** | **0.6389 ms** | **1.874 ms** |
| **GetDocumentInformationByFilterRun** | **10** | **3.868 ms** | **0.6174 ms** | **1.820 ms** |
| **GetDocumentInformationByFilterRun** | **100** | **8.027 ms** | **1.0094 ms** | **2.976 ms** |
| **GetDocumentInformationByFilterRun** | **1000** | **41.938 ms** | **7.9785 ms** | **23.525 ms** |
| **GetDocumentInformationByFilterRun** | **10000** | **337.205 ms** | **51.8841 ms** | **152.167 ms** |
| **GetDocumentInformationByFilterRun** | **100000** | **3,429.605 ms** | **632.3863 ms** | **1,864.605 ms** |
| **GetDocumentInformationByFilterRun** | **250000** | **7,896.811 ms** | **1,431.8274 ms** | **4,221.775 ms** |
| **GetDocumentInformationByFilterRun** | **500000** | **16,629.300 ms** | **2,749.5569 ms** | **8,107.131 ms** |
| **GetDocumentInformationByFilterRun** | **750000** | **23,883.445 ms** | **4,083.3474 ms** | **11,911.316 ms** |
| **GetDocumentInformationByFilterRun** | **1000000** | **32,725.371 ms** | **5,751.8744 ms** | **16,778.487 ms** |
![](./ResultPlots/2022.09.12-localmongodb-oldregex-GetDocumentInformationByFilterLoadTest-boxplot.png =500x)
### 2022.09.12 - local mongodb - "new" regex matching
``` ini
BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19042.1826/20H2/October2020Update)
11th Gen Intel Core i5-1145G7 2.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.400
[Host] : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
Job-ZGOEWK : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
InvocationCount=1 UnrollFactor=1
```
| Method | DocumentCount | Mean | Error | StdDev | Median |
|---------------------------------- |-------------- |--------------:|--------------:|--------------:|--------------:|
| **GetDocumentInformationByFilterRun** | **1** | **3.924 ms** | **0.6009 ms** | **1.724 ms** | **3.724 ms** |
| **GetDocumentInformationByFilterRun** | **10** | **3.675 ms** | **0.6997 ms** | **2.063 ms** | **3.096 ms** |
| **GetDocumentInformationByFilterRun** | **100** | **5.679 ms** | **0.8358 ms** | **2.464 ms** | **5.332 ms** |
| **GetDocumentInformationByFilterRun** | **1000** | **35.936 ms** | **5.6629 ms** | **16.697 ms** | **37.860 ms** |
| **GetDocumentInformationByFilterRun** | **10000** | **305.437 ms** | **53.4368 ms** | **157.560 ms** | **291.339 ms** |
| **GetDocumentInformationByFilterRun** | **100000** | **2,946.267 ms** | **483.7081 ms** | **1,418.632 ms** | **2,793.991 ms** |
| **GetDocumentInformationByFilterRun** | **250000** | **8,456.390 ms** | **1,315.9931 ms** | **3,796.941 ms** | **8,575.654 ms** |
| **GetDocumentInformationByFilterRun** | **500000** | **15,775.558 ms** | **2,830.8954 ms** | **8,167.781 ms** | **15,527.663 ms** |
| **GetDocumentInformationByFilterRun** | **750000** | **24,948.355 ms** | **3,861.0450 ms** | **11,323.777 ms** | **25,630.684 ms** |
| **GetDocumentInformationByFilterRun** | **1000000** | **30,470.661 ms** | **5,342.0572 ms** | **15,583.032 ms** | **29,073.994 ms** |
![](./ResultPlots/2022.09.12-localmongodb-newregex-GetDocumentInformationByFilterLoadTest-boxplot.png =500x)
### 2022.09.28 - indexing strategies
``` ini
BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19042.1826/20H2/October2020Update)
11th Gen Intel Core i5-1145G7 2.60GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.400
[Host] : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
ShortRun : .NET 6.0.8 (6.0.822.36306), X64 RyuJIT AVX2
Job=ShortRun IterationCount=3 LaunchCount=1
WarmupCount=3
```
| Method | Strategy | Mean | Error | StdDev |
|---------------------- |-------------- |----------:|---------:|---------:|
| **DocumentSearch_ByName** | **Default** | **171.51 ms** | **68.40 ms** | **3.749 ms** |
| **DocumentSearch_ByName** | **FieldSpecific** | **91.36 ms** | **29.19 ms** | **1.600 ms** |
| **DocumentSearch_ByName** | **Wildcard** | **94.74 ms** | **78.58 ms** | **4.307 ms** |
![](/ResultPlots/2022.09-28-LCU.API.Internal.Document.PerformanceTest.IndexingStrategyPerformanceTest-boxplot.png)
## required tools
- dotnet 6+
-- `choco install dotnet`
-- probably installed with visual studio
- R
-- `choco install r.project` then add "C:\Program Files\R\R-4.2.1\bin" to your path, assuming you used the default install directory and the version is 4.2.1
--- better to manually add to the path because setx will only give you 1024 characters. Your path is likely already longer than that and will get truncated.
-- this is for outputting visual graphs, not just text tables into standard out
## output
- written to the console
- `.\BenchmarkDotNet.Artifacts` relative to where ever you ran the code from
### graphs
The plots should be produced automatically and you'll find them in the results folder.
If not you can manually generate them:
- `cd .\BenchmarkDotNet.Artifacts\results`
- `rscript BuildPlots.R`
## benchmarkdotnet docs
https://benchmarkdotnet.org/
## gotchas
### Debugging
This will be more difficult than you might be accustomed to. By default benchmark dotnet takes control of a lot of parts of the runtime and even rebuilds some parts of the assembly to ensure isolation and consistent optimzation. See their docs for enabling debugging.
It's possible to run or attach to a release optimized assembly. Some of the things you might expect to be there won't be availabe to the debugger; you're missing debug symbols and the IL has been optimized.
There's always good old console output and file logging.
You might also want to try setting up the problematic scenario in a unit test and then debug *that* instead of debugging the benchmark itself.
### Singletons
Benchmarkdotnet ends up invoking msbuild in order to build separate assemblies (for controlling optimization and isolation). This means that, e.g. `Environment.Type = EnvironmentType.Azure;` in program.cs will not affect the benchmarks when they are run; Environment.Type will be recreated every benchmark because benchmarks are running in a different assembly than the `BenchmarkRunner.Run<T>();` statement which caused the benchmark to run.
One option to deal with this is setting the option inside the benchmark class e.g. in a static constructor (ensuring the value is set before anything else). There are almost surely other options, perhaps a way to pass arguments (other than `[Params()]`) to the benchmark class.
### Azure Rate Limiting
Despite having an Azure environment with code to spin it up and tear it down trying to run a benchmark against a real Azure CosmosDB will likely run you into rate limiting issues. Specifically inserting enough documents to produce a notable database load in a short amount of time. As of 2022.09.13 the rate limit is 1000 records per 5 minutes... You can see where this might produce a problem trying to produce a database on the order of hundreds of thousands of records.
See https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/request-limits-and-throttling#network-throttling for current information.