synchronous send/reply in generic host - nservicebus

I am trying to use synchronous send/reply from the handler function of the generic host windows service as below. But I think NServiceBus will send the message only after completing the handle function(during the current transaction complete). So below code will hang in ‘synchronousHandle.AsyncWaitHandle.WaitOne()’.
What should be the best approach here? Could you please guide me…
Handler constructer
ConstructorFunction(bus)
{
Bus = bus
}
code in the handle function.
// sent the message to the bus and wait for the reply
IMessage response = null;
var synchronousHandle = Bus.Send(service2queue, requestMessage)
.Register(
(AsyncCallback)delegate(IAsyncResult asyncResult)
{
// Callback block for reply message
// Reply message received
NServiceBus.CompletionResult completionResult = asyncResult.AsyncState as NServiceBus.CompletionResult;
if (completionResult != null && completionResult.Messages.Length > 0)
{
// Always expecting one IMessage as reply
response = completionResult.Messages[0];
}
},
null);
// block the current thread till the reply received.
synchronousHandle.AsyncWaitHandle.WaitOne();
Thanks,
Ajai

nservicebus tries to make things as hard as possible when they shouldn't be done.
from the nservicebus documentation:
Bus.Send(request).Register(asyncCallback, state)
Callback only fires on first response, then is cleaned up to prevent memory leaks.
Doesn’t survive restarts – not suitable for server-side
assuming that you are on a server side (am guessing here because you showed us a messagehandler) i would considering a redesign.
service1 gets a notification about messageA
service1 sends message requestMessage to service2
service2 replies with message responseMessage to service1
service1 handles responseMessage and continues processing
if you want to wait for multiple messages in service1 before continuing the processing try considering to implement sagas.

Related

WCF: Method return a response, after spawning a thread to do background processing

So I have the following scenario. I have a method in my WCF, where the client will send a request, the WCF service would then perform some background processing and do call an external webservice method, and the method will respond with an acknowledgement immediately (before the background processing has been completed).
The way I have thought of doing is having my WCF method return a response after spawning a thread to do the background processing, and call the external webservice. The flow is something like this:
Caller sends request to INITIAL_CALL
WCF starts a thread that calls PROCESS
WCF returns true
PROCESS makes call to EXTERNALWS and gets response in postResponse
postReponse gets logged to the database
See example code below:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service : IService
public bool INITIAL_CALL()
{
new Thread(()=>
{
PROCESS();
}).Start();
return true;
}
private void PROCESS()
{
//Do some background processing and create request for call below
var processRequest = "Request goes here";
using (var client = new EXTERNALWS.ResponseTypeClient())
{
var postResponse = client.POST(processRequest);
//Log postResponse to database
}
}
Having in mind that PROCESS() may run for a long time, I just wanted to see if there is a better way of doing this with WCF and IIS? Or if there are any pitfalls that I have to consider i.e IIS app pool recycling destroying the thread.
I have found a solution for this. I ended up using Hangfire to do the background processing needed (https://www.hangfire.io/). Hangfire seems to be specifically made for this. I have implemented it following the documentation found at their homepage, in a separate ASP MVC application. I have also configured it as always running on IIS. All instructions and sample codes to setup Hangfire to do this are found here https://docs.hangfire.io/en/latest/index.html. I had to change the flow (since I am not spawning any new threads manually as previously), and also create a new table in the database so that the INITIAL_CALL in the WCF Application would queue all the long running jobs (later to be picked up and executed by Hangfire). Have in mind this is seperate from Hangfire's queue, this table will be checked by Hangfire in a predefined interval, and will check this database table that stores which function to call, its parameters, and an indicator if the job has already been picked up by Hangfire or not (to avoid the re-entrant scenario described here https://docs.hangfire.io/en/latest/best-practices.html). A little extra work, but works like a charm.
The way the flow works now is as follows:
Caller sends request to INITIAL_CALL
In INITIAL_CALL, an entry is made in a new database table (this is the job
queue that will be checked by Hangfire in a predefined interval).
INITIAL_CALL returns true
Hangfire checks this database table in a predefined interval using PROCESS_JOBS (this interval can be defined in Hangfire itself).
If there is a queued item, PROCESS_JOBS proceeds and makes the call to EXTERNALWS and gets response in postResponse. If not, it just exits and does nothing further.
postReponse gets logged to the database.
See updated example code below:
WCF Application
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service : IService
public bool INITIAL_CALL()
{
//Add job queue entry in database table to be picked up by Hangfire
return true;
}
Hangfire Application
public void PROCESS_JOBS()
{
//Check in a predefined interval if there is a pending job in the queue.
//If there is continue with below, otherwise exit function.
//Do some background processing and create request for call below
var processRequest = "Request goes here";
using (var client = new EXTERNALWS.ResponseTypeClient())
{
var postResponse = client.POST(processRequest);
//Log postResponse to database
}
}

NServiceBus Send() vs SendLocal() and exceptions

We are implementing a saga that calls out to other services with NServiceBus. I'm not quite clear about how NServiceBus deals with exceptions inside a saga.
Inside the saga we have a handler, and that handler calls an external service that should only be called once the original message handler completes succesfully. Is it okay to do:
public void Handle(IFooMessage message)
{
var message = Bus.CreateInstance<ExternalService.IBarMessage>();
Bus.Send(message);
// something bad happens here, exception is thrown
}
or will the message be sent to ExternalService multiple times? Someone here has suggested changing it to:
// handler in the saga
public void Handle(IFooMessage message)
{
// Do something
var message = Bus.CreateInstance<ISendBarMessage>();
Bus.SendLocal(message);
// something bad happens, exception is thrown
}
// a service-level handler
public void Handle(ISendBarMessage message)
{
var message = Bus.CreateInstance<ExternalService.IBarMessage>();
Bus.Send(message);
}
I've done an experiment and from what I can tell the first method seems fine, but I can't find any documentation other than http://docs.particular.net/nservicebus/errors/ which says:
When an exception bubbles through to the NServiceBus infrastructure, it rolls back the transaction on a transactional endpoint, causing the message to be returned to the queue, and any messages that user code tried to send or publish to be undone as well.
Any help to clarify this point would be much appreciated.
As long as you're doing messaging from your saga and not doing any web service calls, then you're safe - no need to do SendLocal.

Do I need to Close and/or Dispose callback channels acquired through OperationContext.Current.GetCallbackChannel?

I'm using OperationContext.Current.GetCallbackChannel to get a channel to the client that called a WCF service operation.
Do I need to worry about closing / disposing these callback channels or is this taken care of by the framework?
Well, I just tried it myself and it turns out that if you Close & Dispose the callback channel (after casting to IClientChannel) the entire Service channel becomes useless and when called throws a ProtocolException saying:
"This channel can no longer be used to send messages as the output session was auto-closed due to a server-initiated shutdown. Either disable auto-close by setting the DispatchRuntime.AutomaticInputSessionShutdown to false, or consider modifying the shutdown protocol with the remote server."
I assume that this is an unwelcome consequence or side effect of attempting to close & dispose the callback channel, meaning that this should not be done.
In my opinion you should.
The callback mechanism supplies nothing like a higher-level protocol for managing the
connection between the service and the callback endpoint. It is up to the developer to
come up with some application-level protocol or a consistent pattern for managing the
lifecycle of the connection. The service can only call back to the client if the client-side channel is still open, which is typically achieved by not closing the proxy. Keeping the proxy open will also prevent the callback object from being garbage-collected. If the service maintains a reference on a callback endpoint and the client-side proxy is closed or the client application itself is gone, when the service invokes the callback it will get an ObjectDisposedException from the service channel. It is therefore preferable for the client to inform the service when it no longer wishes to receive callbacks or when the client application is shutting down. To that end, you can add an explicit Disconnect() method to the service contract. Since every method call carries the callback reference with it, in the Disconnect() method the service can remove the callback reference from its internal store.
here is an exemple :
class MyService : IServiceContract
{
static List<IServiceContractCallback> m_Callbacks = new List<IServiceContractCallback>();
public void Connect()
{
IServiceContractCallbackcallback = OperationContext.Current.GetCallbackChannel<IServiceContractCallback>();
if(m_Callbacks.Contains(callback) == false)
{
m_Callbacks.Add(callback);
}
}
public void Disconnect()
{
IServiceContractCallback callback = OperationContext.Current.GetCallbackChannel<IServiceContractCallback>();
if(m_Callbacks.Contains(callback))
{
m_Callbacks.Remove(callback);
}
else
{
throw new InvalidOperationException("Cannot find callback");
}
}
In such a way a client can inform the service that the callback is no longer needed. Does it answer your question ?

WCF Publish / Subscribe: How to handle client side timeout so as not to miss information?

I have a simple WCF publish/subscribe up and running, based on this example. I am using netTcpBinding with reliableSession enabled. Everything works fine with the functionality (the subscribed clients receive the published data as expected), but at some point the connection times out if it has been idle for a while. I can set up the publisher to reconnect on timeout, but the subscribed clients will be lost. Is there a way to get them back? I would prefer not to just increase the timeouts, as that could cause other problems.
The solution I eventually came up with was to assign a unique identifier to every message that was published, and cache the published message in the service adapter (in the same place where I was storing the callbacks to the subscribed clients. Whenever I published the message, subscribers would receive the message and the corresponding unique id. Subscribers could then use the channel.Faulted event to reconnect and resubscribe to the service with a special method that takes the last received message id as a parameter.
Service code:
/// <summary>
/// Operation used by the subscriber to subscribe to events published.
/// </summary>
public void Resubscribe(int lastReceivedMessageId)
{
// Get callback contract
IPubSubCallback callback = OperationContext.Current.GetCallbackChannel<IPubSubCallback>();
ThreadPool.QueueUserWorkItem(delegate(object state)
{
adapter.Resubscribe(lastReceivedMessageId, callback);
});
}
Adapter code:
/// <summary>
/// Operation used by the subscriber to resubscribe to events published.
/// </summary>
public void Resubscribe(int lastReceivedMessageId, IPubSubCallback callback)
{
try
{
// Send the subscriber any missed messages
foreach (KeyValuePair<int, string> missedMessage in publishedMessages.Where(x => x.Key > lastReceivedMessageId))
{
callback.MessagePublished(missedMessage.Value, missedMessage.Key);
}
// Add the subscriber callback to the list of active subscribers
if (!callbacks.Contains(callback))
{
callbacks.Add(callback);
}
}
catch
{
// ignore subscription, callbacks failed again
}
}
The service can then work out what the client has missed, and resend those messages in the correct order.
This solution seems to be working well for me, but I have a feeling that there must be a better way to do this. Comments / additional answers are very welcome! :)

WCF nested Callback

The backgound: I am trying to forward the server-side ApplyChangeFailed event that is fired by a Sync Services for ADO 1.0 DBServerSyncProvider to the client. All the code examples for Sync Services conflict resolution do not use WCF, and when the client connects to the server database directly, this problem does not exist. My DBServerSyncProvider is wrapped by a head-less WCF service, however, and I cannot show the user a dialog with the offending data for review.
So, the obvious solution seemed to be to convert the HTTP WCF service that Sync Services generated to TCP, make it a duplex connection, and define a callback handler on the client that receives the SyncConflict object and sets the Action property of the event.
When I did that, I got a runtime error (before the callback was attempted):
System.InvalidOperationException: This operation would deadlock because the
reply cannot be received until the current Message completes processing. If
you want to allow out-of-order message processing, specify ConcurrencyMode of
Reentrant or Multiple on CallbackBehaviorAttribute.
So I did what the message suggested and decorated both the service and the callback behavior with the Multiple attribute. Then the runtime error went away, but the call results in a "deadlock" and never returns. What do I do to get around this? Is it not possible to have a WCF service that calls back the client before the original service call returns?
Edit: I think this could be the explanation of the issue, but I am still not sure what the correct solution should be.
After updating the ConcurrencyMode have you tried firing the callback in a seperate thread?
This answer to another question has some example code that starts another thread and passes through the callback, you might be able to modify that design for your purpose?
By starting the sync agent in a separate thread on the client, the callback works just fine:
private int kickOffSyncInSeparateThread()
{
SyncRunner syncRunner = new SyncRunner();
Thread syncThread = new Thread(
new ThreadStart(syncRunner.RunSyncInThread));
try
{
syncThread.Start();
}
catch (ThreadStateException ex)
{
Console.WriteLine(ex);
return 1;
}
catch (ThreadInterruptedException ex)
{
Console.WriteLine(ex);
return 2;
}
return 0;
}
And this is my SyncRunner:
class SyncRunner
{
public void RunSyncInThread()
{
MysyncAgent = new MySyncAgent();
syncAgent.addUserIdParameter("56623239-d855-de11-8e97-0016cfe25fa3");
Microsoft.Synchronization.Data.SyncStatistics syncStats =
syncAgent.Synchronize();
}
}