From c234e1d2804ea0299160ed8e86997fbe0deb9278 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Wed, 19 Aug 2026 13:59:14 +0800 Subject: [PATCH 1/3] Enable loading region from config Similar to how resource-id is handled --- src/AppCommon/Commands/AzureServiceBusCommand.cs | 9 +++++++-- src/Tool/Properties/launchSettings.json | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/AppCommon/Commands/AzureServiceBusCommand.cs b/src/AppCommon/Commands/AzureServiceBusCommand.cs index 27608dce..bc926296 100644 --- a/src/AppCommon/Commands/AzureServiceBusCommand.cs +++ b/src/AppCommon/Commands/AzureServiceBusCommand.cs @@ -54,12 +54,17 @@ public static Command CreateCommand() var cancellationToken = context.GetCancellationToken(); #if DEBUG + // So we don't have to keep an Azure Service Bus resource id and region in launchSettings.json + // Create a local.settings.json file with the keys below. if (resourceId == "LOAD_FROM_CONFIG") { - // So we don't have to keep an Azure Service Bus resource id in launchSettings.json - // Create a local.settings.json file with the key below. resourceId = AppConfig.Get("AZURESERVICEBUS_RESOURCE_ID"); } + + if (region == "LOAD_FROM_CONFIG") + { + region = AppConfig.Get("AZURESERVICEBUS_REGION"); + } #endif var runner = new AzureServiceBusCommand(shared, resourceId, serviceBusDomain, region, metricsDomain); diff --git a/src/Tool/Properties/launchSettings.json b/src/Tool/Properties/launchSettings.json index 235cda47..16bb22e0 100644 --- a/src/Tool/Properties/launchSettings.json +++ b/src/Tool/Properties/launchSettings.json @@ -30,7 +30,7 @@ }, "ThroughputTool-ASB": { "commandName": "Project", - "commandLineArgs": "azureservicebus --resourceId LOAD_FROM_CONFIG --queueNameMasks Samples --customerName \"Particular Software\" --unattended", + "commandLineArgs": "azureservicebus --resourceId LOAD_FROM_CONFIG --region LOAD_FROM_CONFIG --queueNameMasks Samples --customerName \"Particular Software\" --unattended", "environmentVariables": { "IS_DEVELOPMENT": "true" } From c0c3a35149c2a4e770e78f8ed5b8884d3c442cc1 Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Wed, 19 Aug 2026 13:59:47 +0800 Subject: [PATCH 2/3] Get 90 days of data in 30 day reporting windows --- .../Commands/AzureServiceBusCommand.cs | 20 +++++++++++++++++-- src/AppCommon/Infra/ReportingWindow.cs | 17 ++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/AppCommon/Infra/ReportingWindow.cs diff --git a/src/AppCommon/Commands/AzureServiceBusCommand.cs b/src/AppCommon/Commands/AzureServiceBusCommand.cs index bc926296..740365a4 100644 --- a/src/AppCommon/Commands/AzureServiceBusCommand.cs +++ b/src/AppCommon/Commands/AzureServiceBusCommand.cs @@ -1,4 +1,5 @@ using System.CommandLine; +using Azure.Monitor.Query.Metrics.Models; using Particular.EndpointThroughputCounter.Infra; using Particular.LicensingComponent.Report; using Particular.ThroughputQuery; @@ -6,6 +7,9 @@ class AzureServiceBusCommand : BaseCommand { + const int MaxDaysToCollect = 90; + const int MaxDaysToCollectInOneQuery = 30; + public static Command CreateCommand() { var command = new Command("azureservicebus", "Measure endpoints and throughput using Azure Service Bus metrics"); @@ -90,7 +94,7 @@ protected override async Task GetData(CancellationToken cancellati try { var endTime = DateOnly.FromDateTime(DateTime.UtcNow); - var startTime = endTime.AddDays(-90); // Azure Monitor only gives a data for a month back, but we ask for more just in case + var startTime = endTime.AddDays(-MaxDaysToCollect); // Azure Monitor only gives a data for a month back, but we ask for more just in case var results = new List(); azure.ResetConnectionQueue(); @@ -102,7 +106,7 @@ protected override async Task GetData(CancellationToken cancellati Out.Write($"Gathering metrics for queue {i + 1}/{queueNames.Length}: {queueName}"); - var metricValues = (await azure.GetMetrics(queueName, startTime, endTime, cancellationToken)).OrderBy(m => m.TimeStamp).ToArray(); + var metricValues = await GetMetricValues(queueName, startTime, endTime, cancellationToken); var maxThroughput = metricValues.Select(timeEntry => timeEntry.Total).Max(); var start = DateOnly.FromDateTime(metricValues.First().TimeStamp.UtcDateTime); @@ -164,6 +168,18 @@ protected override async Task GetData(CancellationToken cancellati } } + async Task GetMetricValues(string queueName, DateOnly start, DateOnly end, CancellationToken cancellationToken) + { + var metricValues = new List(); + + foreach (var (startTime, endTime) in ReportingWindow.GetReportingWindow(start, end, MaxDaysToCollectInOneQuery)) + { + metricValues.AddRange(await azure.GetMetrics(queueName, startTime, endTime, cancellationToken)); + } + + return [.. metricValues.OrderBy(x => x.TimeStamp)]; + } + protected override async Task GetEnvironment(CancellationToken cancellationToken = default) { Out.WriteLine($"Getting data from {azure.FullyQualifiedNamespace}..."); diff --git a/src/AppCommon/Infra/ReportingWindow.cs b/src/AppCommon/Infra/ReportingWindow.cs new file mode 100644 index 00000000..a63ec08a --- /dev/null +++ b/src/AppCommon/Infra/ReportingWindow.cs @@ -0,0 +1,17 @@ +public static class ReportingWindow +{ + public static IEnumerable<(DateOnly Start, DateOnly End)> GetReportingWindow(DateOnly startDate, DateOnly endDate, int maxDaysPerPeriod) + { + DateOnly currentStart = startDate; + while (currentStart <= endDate) + { + DateOnly currentEnd = currentStart.AddDays(maxDaysPerPeriod); + if (currentEnd > endDate) + { + currentEnd = endDate; + } + yield return (currentStart, currentEnd); + currentStart = currentEnd.AddDays(1); + } + } +} \ No newline at end of file From dd70fce19cff8b8659e7487641580994acc85c0a Mon Sep 17 00:00:00 2001 From: Mike Minutillo Date: Thu, 20 Aug 2026 10:22:30 +0800 Subject: [PATCH 3/3] Shift comment This is consistent to how this is documented in ServiceControl --- src/AppCommon/Commands/AzureServiceBusCommand.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AppCommon/Commands/AzureServiceBusCommand.cs b/src/AppCommon/Commands/AzureServiceBusCommand.cs index 740365a4..dac1730c 100644 --- a/src/AppCommon/Commands/AzureServiceBusCommand.cs +++ b/src/AppCommon/Commands/AzureServiceBusCommand.cs @@ -7,6 +7,7 @@ class AzureServiceBusCommand : BaseCommand { + // ASB keeps 90 days of data but will only return 30 days in a single query const int MaxDaysToCollect = 90; const int MaxDaysToCollectInOneQuery = 30; @@ -94,7 +95,7 @@ protected override async Task GetData(CancellationToken cancellati try { var endTime = DateOnly.FromDateTime(DateTime.UtcNow); - var startTime = endTime.AddDays(-MaxDaysToCollect); // Azure Monitor only gives a data for a month back, but we ask for more just in case + var startTime = endTime.AddDays(-MaxDaysToCollect); var results = new List(); azure.ResetConnectionQueue();