This repository has been archived by the owner on Aug 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Service Bus] Add SubscriptionProcessor (#74)
Added a `SubscriptionProcessor` to the `NuGet.Services.ServiceBus` project. Its purpose is to facilitate processing a specific message type from a given Service Bus subscription. This is similar to @agr's [`Orchestrator`](https://github.com/NuGet/NuGet.Jobs/pull/226/files#diff-0bcef9abb72b34c9e7c76d551f9ae9cd). The pattern to use this class should follow the Orchestrator’s code: 1. A job should call the `SubscriptionProcessor`’s `Start` method within its `Run` method 2. A job should “block” the `Run`’s thread 3. On shutdown, a job should: a. Call the `SubscriptionProcessor`’s `StartShutdownAsync` method b. Wait until the `SubscriptionProcessor`’s `NumberOfMessagesInProgress` goes to 0 This will be used by the Package Signing jobs to listen to Service Bus queues and process validation requests.
- Loading branch information
1 parent
e7ef8ee
commit 1465c68
Showing
16 changed files
with
448 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
src/NuGet.Services.Contracts/ServiceBus/IOnMessageOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace NuGet.Services.ServiceBus | ||
{ | ||
public interface IOnMessageOptions | ||
{ | ||
bool AutoComplete { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
src/NuGet.Services.ServiceBus/IBrokeredMessageSerializer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace NuGet.Services.ServiceBus | ||
{ | ||
/// <summary> | ||
/// Serializes objects into Service Bus <see cref="IBrokeredMessage"/>. | ||
/// </summary> | ||
/// <typeparam name="TMessage">The type this serializer can serialize into <see cref="IBrokeredMessage"/>.</typeparam> | ||
public interface IBrokeredMessageSerializer<TMessage> | ||
{ | ||
/// <summary> | ||
/// Serialize a <see cref="TMessage"/> into a <see cref="IBrokeredMessage"/>. | ||
/// </summary> | ||
/// <param name="message">The message to be serialized.</param> | ||
/// <returns>The serialized message.</returns> | ||
IBrokeredMessage Serialize(TMessage message); | ||
|
||
/// <summary> | ||
/// Deserialize a <see cref="IBrokeredMessage"/> into a <see cref="TMessage"/>. | ||
/// </summary> | ||
/// <param name="message">The message to be deserialized.</param> | ||
/// <returns>The deserialized message.</returns> | ||
TMessage Deserialize(IBrokeredMessage message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Threading.Tasks; | ||
|
||
namespace NuGet.Services.ServiceBus | ||
{ | ||
/// <summary> | ||
/// The class that handles messages received by a <see cref="ISubscriptionProcessor{TMessage}"/> | ||
/// </summary> | ||
/// <typeparam name="TMessage">The type of messages this handler handles.</typeparam> | ||
public interface IMessageHandler<TMessage> | ||
{ | ||
/// <summary> | ||
/// Handle the message. | ||
/// </summary> | ||
/// <param name="message">The received message.</param> | ||
/// <returns>Whether the message has been handled. If false, the message will be requeued to be handled again later.</returns> | ||
Task<bool> HandleAsync(TMessage message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Threading.Tasks; | ||
|
||
namespace NuGet.Services.ServiceBus | ||
{ | ||
/// <summary> | ||
/// Processes messages that were received from a Service Bus subscription. | ||
/// </summary> | ||
/// <typeparam name="TMessage">The type of message listened by this listener.</typeparam> | ||
public interface ISubscriptionProcessor<TMessage> | ||
{ | ||
/// <summary> | ||
/// The number of messages that are currently being handled. | ||
/// </summary> | ||
int NumberOfMessagesInProgress { get; } | ||
|
||
/// <summary> | ||
/// Start handling messages emitted to the Service Bus subscription. | ||
/// </summary> | ||
void Start(); | ||
|
||
/// <summary> | ||
/// Deregisters the message handler. | ||
/// </summary> | ||
/// <remarks> | ||
/// There may still be messages in progress after the returned <see cref="Task"/> has completed! | ||
/// The <see cref="NumberOfMessagesInProgress"/> property should be polled to determine when all | ||
/// messages have been completed. | ||
/// </remarks> | ||
/// <returns>A task that completes when the message handler has been deregistered.</returns> | ||
Task StartShutdownAsync(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using Microsoft.ServiceBus.Messaging; | ||
|
||
namespace NuGet.Services.ServiceBus | ||
{ | ||
public sealed class OnMessageOptionsWrapper : IOnMessageOptions | ||
{ | ||
public OnMessageOptions OnMessageOptions { get; set; } | ||
|
||
public bool AutoComplete | ||
{ | ||
get => OnMessageOptions.AutoComplete; | ||
set => OnMessageOptions.AutoComplete = value; | ||
} | ||
|
||
public OnMessageOptionsWrapper(OnMessageOptions options = null) | ||
{ | ||
OnMessageOptions = OnMessageOptions ?? new OnMessageOptions(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.ServiceBus.Messaging; | ||
|
||
namespace NuGet.Services.ServiceBus | ||
{ | ||
public class SubscriptionProcessor<TMessage> : ISubscriptionProcessor<TMessage> | ||
{ | ||
private readonly ISubscriptionClient _client; | ||
private readonly IBrokeredMessageSerializer<TMessage> _serializer; | ||
private readonly IMessageHandler<TMessage> _handler; | ||
private readonly ILogger<SubscriptionProcessor<TMessage>> _logger; | ||
|
||
private int _numberOfMessagesInProgress; | ||
|
||
public int NumberOfMessagesInProgress => _numberOfMessagesInProgress; | ||
|
||
/// <summary> | ||
/// Constructs a new subscription processor. | ||
/// </summary> | ||
/// <param name="client">The client used to receive messages from the subscription.</param> | ||
/// <param name="serializer">The serializer used to deserialize received messages.</param> | ||
/// <param name="handler">The handler used to handle received messages.</param> | ||
/// <param name="logger">The logger used to record debug information.</param> | ||
public SubscriptionProcessor( | ||
ISubscriptionClient client, | ||
IBrokeredMessageSerializer<TMessage> serializer, | ||
IMessageHandler<TMessage> handler, | ||
ILogger<SubscriptionProcessor<TMessage>> logger) | ||
{ | ||
_client = client ?? throw new ArgumentNullException(nameof(client)); | ||
_serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); | ||
_handler = handler ?? throw new ArgumentNullException(nameof(handler)); | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
|
||
_numberOfMessagesInProgress = 0; | ||
} | ||
|
||
public void Start() | ||
{ | ||
_logger.LogInformation("Registering the handler to begin listening to the Service Bus subscription"); | ||
|
||
_client.OnMessageAsync(OnMessageAsync, new OnMessageOptionsWrapper | ||
{ | ||
AutoComplete = false, | ||
}); | ||
} | ||
|
||
private async Task OnMessageAsync(IBrokeredMessage brokeredMessage) | ||
{ | ||
Interlocked.Increment(ref _numberOfMessagesInProgress); | ||
|
||
try | ||
{ | ||
_logger.LogInformation("Received message from Service Bus subscription, processing"); | ||
|
||
var message = _serializer.Deserialize(brokeredMessage); | ||
|
||
if (await _handler.HandleAsync(message)) | ||
{ | ||
_logger.LogInformation("Message was successfully handled, marking the brokered message as completed"); | ||
|
||
await brokeredMessage.CompleteAsync(); | ||
} | ||
else | ||
{ | ||
_logger.LogInformation("Handler did not finish processing message, requeueing message to be reprocessed"); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.LogError("Requeueing message as it was unsuccessfully processed due to exception: {Exception}", e); | ||
throw; | ||
} | ||
finally | ||
{ | ||
Interlocked.Decrement(ref _numberOfMessagesInProgress); | ||
} | ||
} | ||
|
||
public async Task StartShutdownAsync() | ||
{ | ||
_logger.LogInformation( | ||
"Shutting down the subscription listener with {NumberOfMessagesInProgress} messages in progress", | ||
NumberOfMessagesInProgress); | ||
|
||
await _client.CloseAsync(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.