As Josh said, you could simply clone the original message, set up the scheduled enqueue time, send the clone and complete the original.
Well, it’s a shame that sending the clone and completing the original are not an atomic operation, so there is a very slim chance of us seeing the original again should the handling process crash at just the wrong moment.
And the other issue is that DeliveryCount
on the clone will always be 1, because this is a brand new message. So we could infinitely resubmit and never get round to dead-lettering this message.
Fortunately, that can be fixed by adding our own resubmit count as a property of the message:
[FunctionName("DelayMessage")]
public static async Task DelayMessage([ServiceBusTrigger("MyQueue", AccessRights.Listen, Connection = "MyConnection")]BrokeredMessage originalMessage,
[ServiceBus("MyQueue", AccessRights.Send, Connection = "MyConnection")]IAsyncCollector<BrokeredMessage> newMessages,TraceWriter log)
{
//handle any kind of error scenerio
int resubmitCount = originalMessage.Properties.ContainsKey("ResubmitCount") ? (int)originalMessage.Properties["ResubmitCount"] : 0;
if (resubmitCount > 5)
{
Console.WriteLine("DEAD-LETTERING");
originalMessage.DeadLetter("Too many retries", $"ResubmitCount is {resubmitCount}");
}
else
{
var newMessage = originalMessage.Clone();
newMessage.ScheduledEnqueueTimeUtc = DateTime.UtcNow.AddMinutes(5);
await newMessages.AddAsync(newMessage);
}
}
For more details, you could refer to this article.
Also, it's quite easy to implement the wait/retry/dequeue next pattern in a LogicApp since this type of flow control is exactly what LogicApps was designed for. Please refer to this SO thread.