Skip to content

Commit 72ca3e2

Browse files
committed
Added support for resubmitting the DLQ messages
1 parent ee6307f commit 72ca3e2

9 files changed

Lines changed: 820 additions & 68 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dotnet build
1818
| Command | Description |
1919
|---------|-------------|
2020
| [purge-dlq](docs/purge-dlq.md) | Purge messages from a dead letter queue |
21+
| [resubmit-dlq](docs/resubmit-dlq.md) | Resubmit messages from a dead letter queue back to the main queue |
2122

2223
## Quick Start
2324

@@ -27,6 +28,12 @@ dotnet run -- purge-dlq -n mynamespace.servicebus.windows.net -q myqueue
2728

2829
# Interactive mode - select which message categories to purge
2930
dotnet run -- purge-dlq -n mynamespace.servicebus.windows.net -q myqueue -i
31+
32+
# Resubmit DLQ messages back to the main queue
33+
dotnet run -- resubmit-dlq -n mynamespace.servicebus.windows.net -q myqueue
34+
35+
# Interactive mode - select which message categories to resubmit
36+
dotnet run -- resubmit-dlq -n mynamespace.servicebus.windows.net -q myqueue -i
3037
```
3138

3239
## Authentication

ServiceBusToolset/Commands/PurgeDlqCommand.cs

Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
using Azure.Identity;
22
using Azure.Messaging.ServiceBus;
3+
using ServiceBusToolset.Models;
34
using ServiceBusToolset.Options;
45
using ServiceBusToolset.Services;
56

67
namespace ServiceBusToolset.Commands;
78

8-
public class PurgeDlqCommand(IServiceBusClientFactory clientFactory, IConsoleOutput output) : BaseCommand<PurgeDlqOptions>(clientFactory, output), ICommand<PurgeDlqOptions>
9+
public class PurgeDlqCommand(
10+
IServiceBusClientFactory clientFactory,
11+
IConsoleOutput output,
12+
IDlqCategoryAnalyzer categoryAnalyzer) : BaseCommand<PurgeDlqOptions>(clientFactory, output), ICommand<PurgeDlqOptions>
913
{
1014
private const int MaxBatchSize = 100;
1115
private static readonly TimeSpan MaxWaitTime = TimeSpan.FromSeconds(5);
1216
private const int EmptyBatchThreshold = 3;
1317

14-
private record DlqCategory(string Label, string DeadLetterReason, int Count);
15-
1618
public async Task<int> ExecuteAsync(PurgeDlqOptions options, CancellationToken cancellationToken = default)
1719
{
1820
var validationError = options.Validate();
@@ -275,7 +277,13 @@ private async Task<int> ExecuteInteractivePurgeAsync(
275277
Output.Info($"Analyzing DLQ for {entityDescription}...");
276278

277279
// Step 1: Peek all messages and build category dictionary
278-
var categories = await BuildCategoryListAsync(client, options, cancellationToken);
280+
var categories = await categoryAnalyzer.AnalyzeCategoriesAsync(
281+
client,
282+
options.Queue,
283+
options.Topic,
284+
options.Subscription,
285+
Output,
286+
cancellationToken);
279287

280288
if (categories.Count == 0)
281289
{
@@ -323,63 +331,6 @@ private async Task<int> ExecuteInteractivePurgeAsync(
323331
return 0;
324332
}
325333

326-
private async Task<List<DlqCategory>> BuildCategoryListAsync(
327-
ServiceBusClient client,
328-
PurgeDlqOptions options,
329-
CancellationToken cancellationToken)
330-
{
331-
await using var receiver = CreateDlqReceiver(client,
332-
options.Queue,
333-
options.Topic,
334-
options.Subscription,
335-
ServiceBusReceiveMode.PeekLock);
336-
337-
var categoryCounts = new Dictionary<(string Label, string Reason), int>();
338-
var totalPeeked = 0;
339-
long? fromSequenceNumber = null;
340-
341-
while (!cancellationToken.IsCancellationRequested)
342-
{
343-
IReadOnlyList<ServiceBusReceivedMessage> messages;
344-
345-
if (fromSequenceNumber.HasValue)
346-
{
347-
messages = await receiver.PeekMessagesAsync(MaxBatchSize, fromSequenceNumber.Value, cancellationToken);
348-
}
349-
else
350-
{
351-
messages = await receiver.PeekMessagesAsync(MaxBatchSize, cancellationToken: cancellationToken);
352-
}
353-
354-
if (messages.Count == 0)
355-
{
356-
break;
357-
}
358-
359-
foreach (var msg in messages)
360-
{
361-
var label = msg.Subject ?? "(none)";
362-
var reason = msg.DeadLetterReason ?? "(none)";
363-
var key = (label, reason);
364-
365-
var count = categoryCounts.GetValueOrDefault(key, 0);
366-
categoryCounts[key] = count + 1;
367-
}
368-
369-
totalPeeked += messages.Count;
370-
fromSequenceNumber = messages[^1].SequenceNumber + 1;
371-
372-
Output.Progress($"Peeked {totalPeeked} messages...");
373-
}
374-
375-
Console.WriteLine();
376-
377-
return categoryCounts
378-
.OrderByDescending(kvp => kvp.Value)
379-
.Select(kvp => new DlqCategory(kvp.Key.Label, kvp.Key.Reason, kvp.Value))
380-
.ToList();
381-
}
382-
383334
private void DisplayCategoryTable(IReadOnlyCollection<DlqCategory> categories)
384335
{
385336
Output.Info("");

0 commit comments

Comments
 (0)