Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class EndpointThroughputSummary
public bool IsKnownEndpoint { get; set; }
public string UserIndicator { get; set; }
public long MaxDailyThroughput { get; set; }
public long MaxMonthlyThroughput { get; set; }
}

public class UpdateUserIndicator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

using ServiceControl.Configuration;

public class ThroughputSettings(string serviceControlQueue, string errorQueue, string transportType, string customerName, string serviceControlVersion)
public class ThroughputSettings(
string serviceControlQueue,
string errorQueue,
string transportType,
string customerName,
string serviceControlVersion)
{
public static readonly SettingsRootNamespace SettingsNamespace = new("LicensingComponent");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,33 @@ public DataStoreBuilder WithThroughput(ThroughputSource? source = null, DateOnly
return this;
}

public DataStoreBuilder WithThroughput(ThroughputData throughput)
{
Endpoint endpoint = endpoints.LastOrDefault() ??
throw new InvalidOperationException(
$"Need to add an endpoint before calling {nameof(WithThroughput)}");

var source = endpoint.Id.ThroughputSource;
if (endpoints.SingleOrDefault(e => e.Id.Name == endpoint.Id.Name && e.Id.ThroughputSource == source) == null)
{
throw new InvalidOperationException(
$"Need to add endpoint {endpoint.Id.Name}:{source} before calling {nameof(WithThroughput)}");
}

var idForThroughput = new EndpointIdentifier(endpoint.Id.Name, source);

if (endpointThroughput.TryGetValue(idForThroughput, out List<ThroughputData> throughputList))
{
throughputList.Add(throughput);
}
else
{
endpointThroughput.Add(idForThroughput, [throughput]);
}

return this;
}

public async Task Build()
{
foreach (Endpoint endpoint in endpoints)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace Particular.LicensingComponent.UnitTests;

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Particular.LicensingComponent.Contracts;
Expand Down Expand Up @@ -81,7 +83,7 @@ await DataStore.CreateBuilder()
[TestCase(ThroughputSource.Audit)]
[TestCase(ThroughputSource.Broker)]
[TestCase(ThroughputSource.Monitoring)]
public async Task Should_return_correct_max_throughput_in_summary_when_data_only_from_one_source(ThroughputSource source)
public async Task Should_return_correct_max_daily_throughput_in_summary_when_data_only_from_one_source(ThroughputSource source)
{
// Arrange
await DataStore.CreateBuilder()
Expand All @@ -106,7 +108,7 @@ await DataStore.CreateBuilder()
}

[Test]
public async Task Should_return_correct_max_throughput_in_summary_when_multiple_sources()
public async Task Should_return_correct_max_daily_throughput_in_summary_when_multiple_sources()
{
// Arrange
await DataStore.CreateBuilder()
Expand Down Expand Up @@ -138,7 +140,44 @@ await DataStore.CreateBuilder()
}

[Test]
public async Task Should_return_correct_max_throughput_in_summary_when_endpoint_has_no_throughput()
public async Task Should_return_correct_max_monthly_throughput_in_summary_when_multiple_sources()
{
// Arrange
await DataStore.CreateBuilder()
.AddEndpoint("Endpoint1", sources: [ThroughputSource.Broker])
.WithThroughput(new ThroughputData([
new EndpointDailyThroughput(new DateOnly(2025, 1, 10), 50),
new EndpointDailyThroughput(new DateOnly(2025, 1, 15), 50),
new EndpointDailyThroughput(new DateOnly(2025, 1, 16), 150),
new EndpointDailyThroughput(new DateOnly(2025, 2, 20), 160),
new EndpointDailyThroughput(new DateOnly(2025, 3, 25), 65),
new EndpointDailyThroughput(new DateOnly(2025, 4, 30), 70),
new EndpointDailyThroughput(new DateOnly(2025, 5, 1), 75)]))
.AddEndpoint("Endpoint2", sources: [ThroughputSource.Broker])
.WithThroughput(new ThroughputData([
new EndpointDailyThroughput(new DateOnly(2025, 1, 10), 60),
new EndpointDailyThroughput(new DateOnly(2025, 1, 15), 65),
new EndpointDailyThroughput(new DateOnly(2025, 5, 20), 165),
new EndpointDailyThroughput(new DateOnly(2025, 3, 25), 65),
new EndpointDailyThroughput(new DateOnly(2025, 9, 30), 70)]))
.Build();

// Act
var summary = await ThroughputCollector.GetThroughputSummary(CancellationToken.None);

// Assert
Assert.That(summary, Is.Not.Null);
Assert.That(summary, Has.Count.EqualTo(2));

Assert.Multiple(() =>
{
Assert.That(summary.First(w => w.Name == "Endpoint1").MaxMonthlyThroughput, Is.EqualTo(250), $"Incorrect MaxDailyThroughput recorded for Endpoint1");
Assert.That(summary.First(w => w.Name == "Endpoint2").MaxMonthlyThroughput, Is.EqualTo(165), $"Incorrect MaxDailyThroughput recorded for Endpoint2");
});
}

[Test]
public async Task Should_return_correct_max_daily_throughput_in_summary_when_endpoint_has_no_throughput()
{
// Arrange
await DataStore.CreateBuilder().AddEndpoint().Build();
Expand All @@ -153,7 +192,7 @@ public async Task Should_return_correct_max_throughput_in_summary_when_endpoint_
}

[Test]
public async Task Should_return_correct_max_throughput_in_summary_when_data_from_multiple_sources_and_name_is_different()
public async Task Should_return_correct_max_daily_throughput_in_summary_when_data_from_multiple_sources_and_name_is_different()
{
// Arrange
await DataStore.CreateBuilder()
Expand Down
5 changes: 3 additions & 2 deletions src/Particular.LicensingComponent/ThroughputCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public async Task<List<EndpointThroughputSummary>> GetThroughputSummary(Cancella
Name = endpointData.Name,
UserIndicator = endpointData.UserIndicator ?? (endpointData.IsKnownEndpoint ? Contracts.UserIndicator.NServiceBusEndpoint.ToString() : string.Empty),
IsKnownEndpoint = endpointData.IsKnownEndpoint,
MaxDailyThroughput = endpointData.ThroughputData.Max()
MaxDailyThroughput = endpointData.ThroughputData.MaxDailyThroughput(),
MaxMonthlyThroughput = endpointData.ThroughputData.MaxMonthlyThroughput()
};

endpointSummaries.Add(endpointSummary);
Expand Down Expand Up @@ -130,7 +131,7 @@ public async Task<SignedReport> GenerateThroughputReport(string spVersion, DateT
EndpointIndicators = endpointData.EndpointIndicators ?? [],
NoDataOrSendOnly = endpointData.ThroughputData.Sum() == 0,
Scope = endpointData.Scope ?? "",
Throughput = endpointData.ThroughputData.Max(),
Throughput = endpointData.ThroughputData.MaxDailyThroughput(),
DailyThroughputFromAudit = endpointData.ThroughputData.FromSource(ThroughputSource.Audit).Select(s => new DailyThroughput { DateUTC = s.DateUTC, MessageCount = s.MessageCount }).ToArray(),
DailyThroughputFromMonitoring = endpointData.ThroughputData.FromSource(ThroughputSource.Monitoring).Select(s => new DailyThroughput { DateUTC = s.DateUTC, MessageCount = s.MessageCount }).ToArray(),
DailyThroughputFromBroker = notAnNsbEndpoint ? [] : endpointData.ThroughputData.FromSource(ThroughputSource.Broker).Select(s => new DailyThroughput { DateUTC = s.DateUTC, MessageCount = s.MessageCount }).ToArray()
Expand Down
18 changes: 17 additions & 1 deletion src/Particular.LicensingComponent/ThroughputDataExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public static IEnumerable<EndpointDailyThroughput> FromSource(this List<Throughp

public static long Sum(this List<ThroughputData> throughputs) => throughputs.SelectMany(t => t).Sum(kvp => kvp.Value);

public static long Max(this List<ThroughputData> throughputs)
public static long MaxDailyThroughput(this List<ThroughputData> throughputs)
{
var items = throughputs.SelectMany(t => t).ToArray();

Expand All @@ -22,6 +22,22 @@ public static long Max(this List<ThroughputData> throughputs)
return 0;
}

public static long MaxMonthlyThroughput(this List<ThroughputData> throughputs)
{
var monthlySums = throughputs
.SelectMany(data => data)
.GroupBy(kvp => $"{kvp.Key.Year}-{kvp.Key.Month}")
.Select(group => group.Sum(kvp => kvp.Value))
.ToArray();

if (monthlySums.Any())
{
return monthlySums.Max();
}

return 0;
}

public static bool HasDataFromSource(this IDictionary<string, IEnumerable<ThroughputData>> throughputPerQueue, ThroughputSource source) =>
throughputPerQueue.Any(queueName => queueName.Value.Any(data => data.ThroughputSource == source && data.Count > 0));
}
1 change: 0 additions & 1 deletion src/ServiceControl.LicenseManagement/LicenseDetails.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace ServiceControl.LicenseManagement
{
using System;
using System.Collections.Generic;
using Particular.Licensing;

public class LicenseDetails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,14 @@ public async Task SaveEndpoint(Endpoint endpoint, CancellationToken cancellation

public async Task<IDictionary<string, IEnumerable<ThroughputData>>> GetEndpointThroughputByQueueName(IList<string> queueNames, CancellationToken cancellationToken)
{
var results = queueNames.ToDictionary(queueName => queueName, queueNames => new List<ThroughputData>() as IEnumerable<ThroughputData>);
var results = queueNames.ToDictionary(queueName => queueName, _ => new List<ThroughputData>() as IEnumerable<ThroughputData>);

var store = await storeProvider.GetDocumentStore(cancellationToken);
using IAsyncDocumentSession session = store.OpenAsyncSession(databaseConfiguration.Name);

var query = session.Query<EndpointDocument>()
.Where(document => document.SanitizedName.In(queueNames))
.Include(builder => builder.IncludeTimeSeries(ThroughputTimeSeriesName));
.Include(builder => builder.IncludeTimeSeries(ThroughputTimeSeriesName, DateTime.UtcNow.AddMonths(-14)));

var documents = await query.ToListAsync(cancellationToken);

Expand Down
7 changes: 5 additions & 2 deletions src/ServiceControl/Licensing/LicensingComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ namespace Particular.ServiceControl;
class LicensingComponent : ServiceControlComponent
{
public override void Configure(Settings settings, ITransportCustomization transportCustomization,
IHostApplicationBuilder hostBuilder) =>
IHostApplicationBuilder hostBuilder)
{
var licenseDetails = LicenseManager.FindLicense().Details;
hostBuilder.AddLicensingComponent(
TransportManifestLibrary.Find(settings.TransportType)?.Name ?? settings.TransportType,
settings.ErrorQueue,
settings.InstanceName,
LicenseManager.FindLicense().Details.RegisteredTo,
licenseDetails.RegisteredTo,
ServiceControlVersion.GetFileVersion());
}

public override void Setup(Settings settings, IComponentInstallationContext context,
IHostApplicationBuilder hostBuilder) =>
Expand Down