Callback that starts Saga and returns Response

Hi,

I am trying to understand if this workflow is possible with NServiceBus Callbacks:

  1. WCF service receives HTTP request
  2. WCF service sends NSB Request (with Callback) to start a Saga
  3. Saga executes a number of steps to retrieve data.
  4. Saga aggregates data
  5. In its last step the Saga somehow returns a NSB Response to the callback in the WCF service.

Is this possible? If so how would I do Step 5?

Thanks for you help.

Sean

Hi Sean,

I think it’s not possible. Callbacks are for simple Request/Response scenario where by design client (in your sample WCF) making Request expects the Response so it’s blocked until receive Response from the receiver. Saga is for more complex scenarios with non-blocking operations so by design client (in your sample WCF) cannot expect the Response or at least immediate response. If you don’t have to have immediate response on the client side you could try to use:

but your scenario sounds like standard collecting data from many places so why not retrieve and aggregate them directly from WCF service receives HTTP request?

1 Like

Thanks Michal. That is very useful information.
I agree with your suggestion about calling and aggregating directly from the WCF service.
However, if (because of other considerations) we did need go down the saga route, do you think there would be any issues with using ReplyToOriginator and passing in the original WCF
callback Replyto address.
(Obviously a saga will be slower than orchestrating directly from the WCF service)

You don’t have to put address calling ReplyToOriginator. NServiceBus will reply automatically to the Endpoint who initialized the Saga instance. In your scenario it will be Endpoint (E1) hosting in WCF. I think you don’t even have to use callbacks:

  • You can send standard Command from E1 to the backend Endpoint (E2) hosted in one of the supported technology - https://docs.particular.net/nservicebus/hosting/.
  • The E2 will process Saga policy and call ReplyToOriginator in the last step.
  • The replaying message (M1) will return to the E1 where you have to have standard Message Handler for the M1 (E1 has to be Full Endpoint not SendOnly)

Still M1 will be handled outside the WCF context which is only for hosting Endpoint Instance.

Hope this help.

Correct, ReplyToOriginator will make sure to populate the NServiceBus.CorrelationId header with the message ID that started the saga to make sure that registered callbacks trigger correctly

1 Like

Thanks. Do I understand correctly, to handle the ReplyToOriginator saga response, the WCF service would need to declare a message handler - rather than use the messageSession.Request<MyResponse>() signature?

It IS possible, a callback is just a blocking request/response mechanism.

You can invoke a saga which does any kind of work. As long as it uses ReplyToOriginator the callback mechanism can correlate the response to the request.

Sagas that aggregate using scatter gather is very common and often just exists a few hundred milliseconds.

You do not need to use a handler. Handlers to process a response should be used for actual asynchronous processes. Meaning, there is not a user in a user-interface ‘waiting’ (blocked) for a response.

There are some concerns:

  • Not receiving a response in a timely fashion does not mean the operation was not succesful, just that it is delayed.
  • Asynchronous processes are better of using a task based user interface of which the user can be updated via for example SignalR.

I’m more concerned about why the callback saga construct is needed. If this is just a complex query you are probably better to just query the storage directly from the UI. It often is an anti-pattern to use request/response messaging to do querying.

Ahh…I checked again my modification of sample and I request for int

await endpointInstance.Request<int>(message, sendOptions)

but response with object

return this.ReplyToOriginator(context, objectResponseMessage);

that’s why I receive No handlers could be found for message type: ObjectResponseMessage in Samples.Callbacks.Sender Endpoint so that’s why I thought the scenario described at the beginning of this Topic is not possible. After fix request to:

await endpointInstance.Request<ObjectResponseMessage>

everything seems to work properly… :blush:.

@Sean-R sorry for the confusion. Just as @ramonsmits said

and if you want to use callbacks you don’t need to declare separate message Handler for the Response from Saga. Otherwise yes you need.

It’s interesting Topic so let’s try to analyze possibilities discussed so far from the failure point of view:

  1. blocking query from WCF (or UI directly) - no NServiceBus
  2. blocking Request from WCF using Callbacks
  3. no-blocking Send from WCF using standard Send/Replay (Request/Response)

At the first point (preferred) there is expectation of immediate Exception when something goes wrong.

At the third point there isn’t this kind of expectation. When something goes wrong (no transient error) the message will be moved to the error queue but the WCF won’t be wait for the response.

At the second point there could be a situation when message will be in the error queue but WCF will be blocked and wait for the response.

@andreasohlund, @ramonsmits is there something like default callback timeout when calling endpointInstance.Request<...>(...) apart from Cancellation or restarting the process?

@Sean-R if you have to use Callbacks you could take to the consideration the failure scenario.

@ramonsmits Thanks for your advice. I agree, it feels like that would be simpler and quicker to call out directly from the WCF service. In our particular scenario, there are other processes that will be using the same saga workflow. I think this is one of the reasons for also wanting to push the web service data through the Saga.
Unfortunately, SignalR is not an option in this case as its an external legacy web service with many clients.

no-blocking Send from WCF using standard Send/Replay (Request/Response)

Interesting. Would option 3 require a MessageHandler hosted in the Web Process? With this approach would it be possible to respond to the original Http request?

  • YES the MessageHandler for response is required otherwise you will receive the similar behavior like my example above.
  • Unfortunately NO after sending the message the WCF Request is closing. As @ramonsmits mentioned it’s possible to use SignalR for updating the user but like you said this is not an option for your scenario.