6
6
using System . Threading . Tasks ;
7
7
using System . Transactions ;
8
8
using Azure . Messaging . ServiceBus ;
9
+ using BitFaster . Caching . Lru ;
9
10
using Extensibility ;
10
11
using Logging ;
11
12
@@ -15,6 +16,7 @@ class MessagePump : IMessageReceiver
15
16
readonly ReceiveSettings receiveSettings ;
16
17
readonly Action < string , Exception , CancellationToken > criticalErrorAction ;
17
18
readonly ServiceBusClient serviceBusClient ;
19
+ readonly FastConcurrentLru < string , bool > messagesToBeDeleted = new ( 1_000 ) ;
18
20
19
21
OnMessage onMessage ;
20
22
OnError onError ;
@@ -120,6 +122,30 @@ int CalculatePrefetchCount()
120
122
return prefetchCount ;
121
123
}
122
124
125
+ #pragma warning disable PS0018
126
+ async Task DeleteMessage ( ProcessMessageEventArgs processMessageEventArgs , ServiceBusReceivedMessage message )
127
+ #pragma warning restore PS0018
128
+ {
129
+ try
130
+ {
131
+ using ( var azureServiceBusTransaction = CreateTransaction ( message . PartitionKey ) )
132
+ {
133
+ await processMessageEventArgs . SafeCompleteMessageAsync ( message ,
134
+ transportSettings . TransportTransactionMode ,
135
+ azureServiceBusTransaction , CancellationToken . None )
136
+ . ConfigureAwait ( false ) ;
137
+
138
+ azureServiceBusTransaction . Commit ( ) ;
139
+ }
140
+ }
141
+ catch ( Exception ex )
142
+ {
143
+ Logger . Warn ( $ "Failed to delete message with id '{ message . GetMessageId ( ) } '. This message will be returned to the queue", ex ) ;
144
+
145
+ messagesToBeDeleted . AddOrUpdate ( message . GetMessageId ( ) , true ) ;
146
+ }
147
+ }
148
+
123
149
#pragma warning disable PS0018
124
150
async Task OnProcessMessage ( ProcessMessageEventArgs arg )
125
151
#pragma warning restore PS0018
@@ -135,6 +161,12 @@ async Task OnProcessMessage(ProcessMessageEventArgs arg)
135
161
{
136
162
messageId = message . GetMessageId ( ) ;
137
163
164
+ if ( messagesToBeDeleted . TryGet ( messageId , out _ ) )
165
+ {
166
+ await DeleteMessage ( arg , message ) . ConfigureAwait ( false ) ;
167
+ return ;
168
+ }
169
+
138
170
if ( processor . ReceiveMode == ServiceBusReceiveMode . PeekLock && message . LockedUntil < DateTimeOffset . UtcNow )
139
171
{
140
172
Logger . Warn (
@@ -297,6 +329,18 @@ await processMessageEventArgs.SafeCompleteMessageAsync(message,
297
329
{
298
330
try
299
331
{
332
+ if ( ex is ServiceBusException serviceBusException && serviceBusException . Reason == ServiceBusFailureReason . MessageLockLost )
333
+ {
334
+ if ( transportSettings . TransportTransactionMode == TransportTransactionMode . ReceiveOnly )
335
+ {
336
+ Logger . Warn ( $ "Message with id `{ messageId } ` has been returned to the queue. NServiceBus recoverability is being skipped. { serviceBusException . Message } ") ;
337
+ messagesToBeDeleted . AddOrUpdate ( messageId , true ) ;
338
+ //Since the message lock was lost, we can't complete or abandon the message without throwing an error
339
+ //Recoverability should be skipped
340
+ return ;
341
+ }
342
+ }
343
+
300
344
ErrorHandleResult result ;
301
345
302
346
using ( var azureServiceBusTransaction = CreateTransaction ( message . PartitionKey ) )
@@ -326,7 +370,7 @@ await processMessageEventArgs.SafeAbandonMessageAsync(message,
326
370
. ConfigureAwait ( false ) ;
327
371
}
328
372
}
329
- catch ( ServiceBusException onErrorEx ) when ( onErrorEx . IsTransient || onErrorEx . Reason is ServiceBusFailureReason . MessageLockLost )
373
+ catch ( ServiceBusException onErrorEx ) when ( onErrorEx . IsTransient )
330
374
{
331
375
Logger . Debug ( "Failed to execute recoverability." , onErrorEx ) ;
332
376
0 commit comments