Skip to content

Commit fcb0280

Browse files
authored
Add a handler startup test to SubscriptionProcessorJob and Orchestrator (#10155)
1 parent 320852f commit fcb0280

4 files changed

Lines changed: 44 additions & 3 deletions

File tree

src/NuGet.Services.Validation.Orchestrator/Job.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ public override async Task Run()
106106

107107
try
108108
{
109+
_serviceProvider.ValidateMessageHandlerInitialization<PackageValidationMessageData>();
110+
109111
var runner = GetRequiredService<OrchestrationRunner>();
110112
await runner.RunOrchestrationAsync();
111113
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Logging;
7+
using NuGet.Services.ServiceBus;
8+
9+
namespace NuGet.Jobs.Validation
10+
{
11+
public static class ServiceProviderExtensions
12+
{
13+
public static void ValidateMessageHandlerInitialization<TMessage>(this IServiceProvider serviceProvider)
14+
{
15+
// To detect any start-up errors, test initializing the actual message processor.
16+
// The message processor initialized in the main path uses a wrapper message handler, which defers initilization until a message arrives.
17+
// This startup test allows the app to crash prior to recieving a message which it wouldn't be able to handle properly anyways.
18+
// Having the app crash in this case is preferable since messages won't be dead-lettered and the job heartbeats will indicate a problem.
19+
using (var scope = serviceProvider.CreateScope())
20+
{
21+
var logger = scope.ServiceProvider.GetRequiredService<ILogger<IMessageHandler<TMessage>>>();
22+
logger.LogInformation("Verifying that the message handler for message type {MessageType} can be resolved.", typeof(TMessage).FullName);
23+
try
24+
{
25+
var handler = scope.ServiceProvider.GetRequiredService<IMessageHandler<TMessage>>();
26+
logger.LogInformation("Successfully initialized message handler type {MessageType}.", handler.GetType().FullName);
27+
}
28+
catch
29+
{
30+
logger.LogError("Failed to initialize message handler for message type {MessageType}. The subscription processor job cannot start. See the exception logs for more details.", typeof(TMessage).FullName);
31+
throw;
32+
}
33+
}
34+
}
35+
}
36+
}

src/Validation.Common.Job/SubscriptionProcessorJob.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -33,18 +33,20 @@ public override async Task Run()
3333

3434
if (processor == null)
3535
{
36-
throw new Exception($"DI container was not set up to produce instances of ISubscriptionProcessor<{typeof(T).Name}>. " +
36+
throw new InvalidOperationException($"DI container was not set up to produce instances of ISubscriptionProcessor<{typeof(T).Name}>. " +
3737
$"Call SubcriptionProcessorJob<T>.{nameof(ConfigureDefaultSubscriptionProcessor)}() or set it up your way.");
3838
}
3939

4040
var configuration = _serviceProvider.GetService<IOptionsSnapshot<SubscriptionProcessorConfiguration>>();
4141

4242
if (configuration == null || configuration.Value == null)
4343
{
44-
throw new Exception($"Failed to get the SubscriptionProcessorJob configuration. Call " +
44+
throw new InvalidOperationException($"Failed to get the SubscriptionProcessorJob configuration. Call " +
4545
$"SubcriptionProcessorJob<T>.{nameof(SetupDefaultSubscriptionProcessorConfiguration)}() or set it up your way.");
4646
}
4747

48+
_serviceProvider.ValidateMessageHandlerInitialization<T>();
49+
4850
await processor.StartAsync(configuration.Value.MaxConcurrentCalls);
4951

5052
// Wait a certain period of time, and then shutdown this process so that it is restarted.

src/Validation.Common.Job/Validation.Common.Job.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</PropertyGroup>
1010

1111
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
12+
<Compile Remove="ServiceProviderExtensions.cs" />
1213
<Compile Remove="Storage\IValidatorStateService.cs" />
1314
<Compile Remove="Storage\ValidatorStateService.cs" />
1415
<Compile Remove="Storage\ValidatorStatusExtensions.cs" />

0 commit comments

Comments
 (0)