Skip to content

Commit d759847

Browse files
authored
Merge pull request #4863 from Particular/john/new-license
Limit throughput data returned to 14 months
2 parents fca1ba5 + a6786e0 commit d759847

File tree

9 files changed

+104
-13
lines changed

9 files changed

+104
-13
lines changed

src/Particular.LicensingComponent.Contracts/EndpointThroughputSummary.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class EndpointThroughputSummary
77
public bool IsKnownEndpoint { get; set; }
88
public string UserIndicator { get; set; }
99
public long MaxDailyThroughput { get; set; }
10+
public long MaxMonthlyThroughput { get; set; }
1011
}
1112

1213
public class UpdateUserIndicator

src/Particular.LicensingComponent.Contracts/ThroughputSettings.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
using ServiceControl.Configuration;
44

5-
public class ThroughputSettings(string serviceControlQueue, string errorQueue, string transportType, string customerName, string serviceControlVersion)
5+
public class ThroughputSettings(
6+
string serviceControlQueue,
7+
string errorQueue,
8+
string transportType,
9+
string customerName,
10+
string serviceControlVersion)
611
{
712
public static readonly SettingsRootNamespace SettingsNamespace = new("LicensingComponent");
813

src/Particular.LicensingComponent.UnitTests/Infrastructure/DataStoreBuilder.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,33 @@ public DataStoreBuilder WithThroughput(ThroughputSource? source = null, DateOnly
9191
return this;
9292
}
9393

94+
public DataStoreBuilder WithThroughput(ThroughputData throughput)
95+
{
96+
Endpoint endpoint = endpoints.LastOrDefault() ??
97+
throw new InvalidOperationException(
98+
$"Need to add an endpoint before calling {nameof(WithThroughput)}");
99+
100+
var source = endpoint.Id.ThroughputSource;
101+
if (endpoints.SingleOrDefault(e => e.Id.Name == endpoint.Id.Name && e.Id.ThroughputSource == source) == null)
102+
{
103+
throw new InvalidOperationException(
104+
$"Need to add endpoint {endpoint.Id.Name}:{source} before calling {nameof(WithThroughput)}");
105+
}
106+
107+
var idForThroughput = new EndpointIdentifier(endpoint.Id.Name, source);
108+
109+
if (endpointThroughput.TryGetValue(idForThroughput, out List<ThroughputData> throughputList))
110+
{
111+
throughputList.Add(throughput);
112+
}
113+
else
114+
{
115+
endpointThroughput.Add(idForThroughput, [throughput]);
116+
}
117+
118+
return this;
119+
}
120+
94121
public async Task Build()
95122
{
96123
foreach (Endpoint endpoint in endpoints)

src/Particular.LicensingComponent.UnitTests/ThroughputCollector/ThroughputCollector_ThroughputSummary_Tests.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
namespace Particular.LicensingComponent.UnitTests;
22

3+
using System;
34
using System.Linq;
5+
using System.Threading;
46
using System.Threading.Tasks;
57
using NUnit.Framework;
68
using Particular.LicensingComponent.Contracts;
@@ -81,7 +83,7 @@ await DataStore.CreateBuilder()
8183
[TestCase(ThroughputSource.Audit)]
8284
[TestCase(ThroughputSource.Broker)]
8385
[TestCase(ThroughputSource.Monitoring)]
84-
public async Task Should_return_correct_max_throughput_in_summary_when_data_only_from_one_source(ThroughputSource source)
86+
public async Task Should_return_correct_max_daily_throughput_in_summary_when_data_only_from_one_source(ThroughputSource source)
8587
{
8688
// Arrange
8789
await DataStore.CreateBuilder()
@@ -106,7 +108,7 @@ await DataStore.CreateBuilder()
106108
}
107109

108110
[Test]
109-
public async Task Should_return_correct_max_throughput_in_summary_when_multiple_sources()
111+
public async Task Should_return_correct_max_daily_throughput_in_summary_when_multiple_sources()
110112
{
111113
// Arrange
112114
await DataStore.CreateBuilder()
@@ -138,7 +140,44 @@ await DataStore.CreateBuilder()
138140
}
139141

140142
[Test]
141-
public async Task Should_return_correct_max_throughput_in_summary_when_endpoint_has_no_throughput()
143+
public async Task Should_return_correct_max_monthly_throughput_in_summary_when_multiple_sources()
144+
{
145+
// Arrange
146+
await DataStore.CreateBuilder()
147+
.AddEndpoint("Endpoint1", sources: [ThroughputSource.Broker])
148+
.WithThroughput(new ThroughputData([
149+
new EndpointDailyThroughput(new DateOnly(2025, 1, 10), 50),
150+
new EndpointDailyThroughput(new DateOnly(2025, 1, 15), 50),
151+
new EndpointDailyThroughput(new DateOnly(2025, 1, 16), 150),
152+
new EndpointDailyThroughput(new DateOnly(2025, 2, 20), 160),
153+
new EndpointDailyThroughput(new DateOnly(2025, 3, 25), 65),
154+
new EndpointDailyThroughput(new DateOnly(2025, 4, 30), 70),
155+
new EndpointDailyThroughput(new DateOnly(2025, 5, 1), 75)]))
156+
.AddEndpoint("Endpoint2", sources: [ThroughputSource.Broker])
157+
.WithThroughput(new ThroughputData([
158+
new EndpointDailyThroughput(new DateOnly(2025, 1, 10), 60),
159+
new EndpointDailyThroughput(new DateOnly(2025, 1, 15), 65),
160+
new EndpointDailyThroughput(new DateOnly(2025, 5, 20), 165),
161+
new EndpointDailyThroughput(new DateOnly(2025, 3, 25), 65),
162+
new EndpointDailyThroughput(new DateOnly(2025, 9, 30), 70)]))
163+
.Build();
164+
165+
// Act
166+
var summary = await ThroughputCollector.GetThroughputSummary(CancellationToken.None);
167+
168+
// Assert
169+
Assert.That(summary, Is.Not.Null);
170+
Assert.That(summary, Has.Count.EqualTo(2));
171+
172+
Assert.Multiple(() =>
173+
{
174+
Assert.That(summary.First(w => w.Name == "Endpoint1").MaxMonthlyThroughput, Is.EqualTo(250), $"Incorrect MaxDailyThroughput recorded for Endpoint1");
175+
Assert.That(summary.First(w => w.Name == "Endpoint2").MaxMonthlyThroughput, Is.EqualTo(165), $"Incorrect MaxDailyThroughput recorded for Endpoint2");
176+
});
177+
}
178+
179+
[Test]
180+
public async Task Should_return_correct_max_daily_throughput_in_summary_when_endpoint_has_no_throughput()
142181
{
143182
// Arrange
144183
await DataStore.CreateBuilder().AddEndpoint().Build();
@@ -153,7 +192,7 @@ public async Task Should_return_correct_max_throughput_in_summary_when_endpoint_
153192
}
154193

155194
[Test]
156-
public async Task Should_return_correct_max_throughput_in_summary_when_data_from_multiple_sources_and_name_is_different()
195+
public async Task Should_return_correct_max_daily_throughput_in_summary_when_data_from_multiple_sources_and_name_is_different()
157196
{
158197
// Arrange
159198
await DataStore.CreateBuilder()

src/Particular.LicensingComponent/ThroughputCollector.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public async Task<List<EndpointThroughputSummary>> GetThroughputSummary(Cancella
7171
Name = endpointData.Name,
7272
UserIndicator = endpointData.UserIndicator ?? (endpointData.IsKnownEndpoint ? Contracts.UserIndicator.NServiceBusEndpoint.ToString() : string.Empty),
7373
IsKnownEndpoint = endpointData.IsKnownEndpoint,
74-
MaxDailyThroughput = endpointData.ThroughputData.Max()
74+
MaxDailyThroughput = endpointData.ThroughputData.MaxDailyThroughput(),
75+
MaxMonthlyThroughput = endpointData.ThroughputData.MaxMonthlyThroughput()
7576
};
7677

7778
endpointSummaries.Add(endpointSummary);
@@ -130,7 +131,7 @@ public async Task<SignedReport> GenerateThroughputReport(string spVersion, DateT
130131
EndpointIndicators = endpointData.EndpointIndicators ?? [],
131132
NoDataOrSendOnly = endpointData.ThroughputData.Sum() == 0,
132133
Scope = endpointData.Scope ?? "",
133-
Throughput = endpointData.ThroughputData.Max(),
134+
Throughput = endpointData.ThroughputData.MaxDailyThroughput(),
134135
DailyThroughputFromAudit = endpointData.ThroughputData.FromSource(ThroughputSource.Audit).Select(s => new DailyThroughput { DateUTC = s.DateUTC, MessageCount = s.MessageCount }).ToArray(),
135136
DailyThroughputFromMonitoring = endpointData.ThroughputData.FromSource(ThroughputSource.Monitoring).Select(s => new DailyThroughput { DateUTC = s.DateUTC, MessageCount = s.MessageCount }).ToArray(),
136137
DailyThroughputFromBroker = notAnNsbEndpoint ? [] : endpointData.ThroughputData.FromSource(ThroughputSource.Broker).Select(s => new DailyThroughput { DateUTC = s.DateUTC, MessageCount = s.MessageCount }).ToArray()

src/Particular.LicensingComponent/ThroughputDataExtensions.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public static IEnumerable<EndpointDailyThroughput> FromSource(this List<Throughp
1010

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

13-
public static long Max(this List<ThroughputData> throughputs)
13+
public static long MaxDailyThroughput(this List<ThroughputData> throughputs)
1414
{
1515
var items = throughputs.SelectMany(t => t).ToArray();
1616

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

25+
public static long MaxMonthlyThroughput(this List<ThroughputData> throughputs)
26+
{
27+
var monthlySums = throughputs
28+
.SelectMany(data => data)
29+
.GroupBy(kvp => $"{kvp.Key.Year}-{kvp.Key.Month}")
30+
.Select(group => group.Sum(kvp => kvp.Value))
31+
.ToArray();
32+
33+
if (monthlySums.Any())
34+
{
35+
return monthlySums.Max();
36+
}
37+
38+
return 0;
39+
}
40+
2541
public static bool HasDataFromSource(this IDictionary<string, IEnumerable<ThroughputData>> throughputPerQueue, ThroughputSource source) =>
2642
throughputPerQueue.Any(queueName => queueName.Value.Any(data => data.ThroughputSource == source && data.Count > 0));
2743
}

src/ServiceControl.LicenseManagement/LicenseDetails.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
namespace ServiceControl.LicenseManagement
22
{
33
using System;
4-
using System.Collections.Generic;
54
using Particular.Licensing;
65

76
public class LicenseDetails

src/ServiceControl.Persistence.RavenDB/Throughput/LicensingDataStore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,14 @@ public async Task SaveEndpoint(Endpoint endpoint, CancellationToken cancellation
122122

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

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

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

134134
var documents = await query.ToListAsync(cancellationToken);
135135

src/ServiceControl/Licensing/LicensingComponent.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ namespace Particular.ServiceControl;
1111
class LicensingComponent : ServiceControlComponent
1212
{
1313
public override void Configure(Settings settings, ITransportCustomization transportCustomization,
14-
IHostApplicationBuilder hostBuilder) =>
14+
IHostApplicationBuilder hostBuilder)
15+
{
16+
var licenseDetails = LicenseManager.FindLicense().Details;
1517
hostBuilder.AddLicensingComponent(
1618
TransportManifestLibrary.Find(settings.TransportType)?.Name ?? settings.TransportType,
1719
settings.ErrorQueue,
1820
settings.InstanceName,
19-
LicenseManager.FindLicense().Details.RegisteredTo,
21+
licenseDetails.RegisteredTo,
2022
ServiceControlVersion.GetFileVersion());
23+
}
2124

2225
public override void Setup(Settings settings, IComponentInstallationContext context,
2326
IHostApplicationBuilder hostBuilder) =>

0 commit comments

Comments
 (0)