Long running Handler operation that can't be batched

Hello,

In part of our application we have a requirement to stream large files from Azure Blob Storage and write them into Azure SQL. They are pretty fast, but sometimes may take some time (beyond 5 minutes for example) to complete. These operations can’t be batched.
The whole business operation is orchestrated by a couple of NSB saga’s on ASB transport and there are other message based operations before and after the streaming operation. So I would like to keep the entire workflow in the NSB world.

I read this article about breaking out long running operations into a stand alone process that uses Azure TableStorage as a pseudo queue. The stand alone process would then send messages back to my saga from outside of a message context. At the end of the streaming the TableStorage message is also manually acknowledged.

I though this would be my preferred approach. But after re-reading the following paragraph about ASB message locks I am not sure if I need to have a stand alone process.

Message lock renewal is initiated by client code, not the broker. If the request to renew the lock fails after all the SDK built-in retries (.e.g due to a connection-loss), the lock won’t be renewed, and the message will become unlocked and available for processing by competing consumers. Lock renewal should be treated as best-effort and not as a guaranteed operation.

As my worker service (the service that is streaming the files) won’t ever be scaled out beyond 1 instance (so no - competing consumers) would I be safe to just use the ASB transport in it and make it a full NSB endpoint and rely on renews (whilst also setting the MaxAutoRenewDuration to a high number)?

Thanks for your help

Good day @Sean-R

Yes, that’s a viable solution.

The article was written before the introduction of the auto-renew feature. The only thing to remember is if that auto-renew could fail, and if it fails for a long time, the message could reappear in the input queue. It’s true that you don’t have competing consumers, but if the endpoint concurrency is higher than one, the same endpoint could be reprocessing the message.

What storage type are you using for sagas?

.m

Thanks Mauro, that’s good to hear that that is an option. I made a start on the standalone endpoint approach - and it has ended up with me having to write more plumbing than I would like, and there are some potential consistency blackspots. For example: at the end of performing it’s operation - my pseudo handler has to acknowledge the TableStorage message, Acknowledge the in memory queue message, and send a response back to the saga.

Interesting point about the endpoint concurrency. It could be throttled to be just 1, but this would limit some of the parallelism I was hoping to introduce. For example - I was hoping the Saga could send out 10 commands to the Worker, and each one streams a blob concurrently. However, I suppose I could send one command and multi thread inside the NSB handler to solve this. I will give that some thought.

I am using SQL Storage for the Sagas.

Thanks

I’d avoid multi-threading in a single message handler like plague. It’s a can of worms, you’d have to keep track of the threads, who’s completed and who’s still in progress. And what to do if one of them fails is a burden.

An option to only use NServiceBus and no pseudo-messaging, that as you noted requires a lot of plumbing is described in this blog post: Transactions? None for me, thanks

Let me know if that helps.
.m

1 Like

The whole comment about lock renewal can be ignored. Both the latest version of our Azure Service Bus transport as well as Azure Functions. Meaning, long running (+5 minutes) handlers will behave as expected.

1 Like

Thanks Mauro, I will avoid manually managed multithreading.