none
Order of messages sent by a WCF service to multiple clients

    Question

  • Hi all,

    I have a multithreaded WCF service with callbacks. Multiple clients can subscribe to the service, and get notified (via callback) when any other client calls a function in the service. Upon receiving the notification, these clients might also decide to call the function, which would cause all other clients (including the original one) to be notified, and so on. My implementation looks something like this (vastly simplified):

    int CallService() { int returnValue = calcReturnValue(); foreach(var client in m_clientList) // note: in the actual program, the current caller gets excluded from this foreach client.Callback.NotifyClient(returnValue); return returnValue; }

    This works, but there's a snag. If the other clients are really fast, they can get the notification and call the service themselves before the first client has received the return value.

    In fact, sometimes it happens that the first client calls the service, and receives a notification of another client calling the service as a consequence of its own call, before its own call has even returned. This last situation is a problem, as stuff is happening out-of-order.

    Now, if I was handling my own TCP/IP channels, I would just send the return value to the caller and then fire the notifications. This would obviously guarantee that the caller gets its response before anything else. But I can't do that in WCF, because you can't execute code after a return statement.

    How would you solve this?

    Tuesday, January 07, 2014 3:53 PM

Answers

  • You can block the client from responding until an ack is received from the main thread.  When you start a listener you have a main thread that starts the listener.  Then when each client connects a new client is created.  The main thread can still do processing.  I would add an await object so each client can communicate to the main thread.  The main thread can then synchronize the clients so only one client will respond at a time.

    jdweng

    Wednesday, January 08, 2014 1:37 PM

All replies

  • Hi,

    Maybe you can set the InstanceContextMode mode as percall. Setting of the InstanceContextMode on your service class (that implements the service contract) has a strong influence on how your service will handle concurrency and multiple requests. then it will allow only one client one call, please try to check this:

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
    class YourServiceClass : IYourService
    {
      .....
    }

    Also please try to check this thread:

    http://stackoverflow.com/questions/13802385/how-to-enforce-message-queue-sequence-with-multiple-wcf-service-instances .

    Best Regards,
    Amy Peng


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.



    Wednesday, January 08, 2014 7:18 AM
  • Hi Amy,

    Thanks for your answer. I absolutely need the service to be able to be called concurrently, for performance reasons, so I can't just force clients to wait on each other. And my binding is net.tcp, not MSMQ, so I don't have the problems described in the other thread.

    I am aware that this requirement makes ordering tricky, but I don't need absolute ordering - I only need the call's return value to be sent to the caller before callbacks are sent to the other clients. I don't really care about what happens in other calls; I don't mind if other clients call the service concurrently, I just need to postpone callbacks until after a call has returned. The binding is net.tcp and there's a single server, so in theory this should be straightforward.

    Indeed, I've implemented the same scenario in straight TCP (no WCF) in another occasion, and this was never a problem: you just put the answer in the caller's sendbuffer before you put the callbacks in the other clients' sendbuffers. That's enough to guarantee that the order of messages, while not absolute, at least respects cause-and-effect from the caller's point of view. Is there any way to do the same thing in WCF?

    Wednesday, January 08, 2014 9:00 AM
  • What is your listener client code?  Why can't you use the TCP code that your have used in the past?


    jdweng

    Wednesday, January 08, 2014 10:57 AM
  • The listener implements the callback interface, which is just something like:

    void OnNotification(int i)
    {
      // do stuff here
    }

    Where "stuff" is something that fails if it arrives in an order that violates cause-and-effect from the POV of the client (i.e. it arrives before that call that triggered it returns). The interface has IsOneWay, and the implementation has UseSynchronizationContext = false and ConcurrencyMode.Multiple. This is necessary because notifications could be received while making a call, and in this scenario if the client was single-threaded it would deadlock.

    The reason I can't use the old TCP code is that it's C++ and this project is C#. Also, this issue is subtle enough that I've spent quite a lot of time on the project before noticing it, and I'm reluctant to trash all of the WCF code I've made before investigating every other option.

    Anyway, I've been meditating on this and I've realised that the issue is trickier than I thought. I've been asking for a way to make the server respect ordering while still being multithreaded, which is theorically easy enough and which is what the TCP app did. But that actually wouldn't solve the issue completely, as the client is also multithreaded so the messages could still get swapped on the client side. This is a considerably nastier problem.

    So - I think there's no generic solution; even if I found a way to make the server behave as I want, I'd still need to make the client single-threaded, which was the solution in the old TCP app. In this case, I'm probably better off figuring out a way to just make the client tolerate apparent cause-and-effect order violations.

    Wednesday, January 08, 2014 1:24 PM
  • You can block the client from responding until an ack is received from the main thread.  When you start a listener you have a main thread that starts the listener.  Then when each client connects a new client is created.  The main thread can still do processing.  I would add an await object so each client can communicate to the main thread.  The main thread can then synchronize the clients so only one client will respond at a time.

    jdweng

    Wednesday, January 08, 2014 1:37 PM
  • Thanks for your answers. If I understand correctly, you suggest having an ack from the client to the server, telling the server that it's OK to send out notifications to the other clients. Makes sense.

    Unfortunately, it also adds a transmission time which I can't spare. So I'll go with an ad-hoc solution of making the client able to deal with out-of-order messages instead. Still, good idea.

    Wednesday, January 08, 2014 2:00 PM
  • If using interupts the delay time would be < 200msec per packet.  You can't have your cake and eat it too.

    jdweng

    Wednesday, January 08, 2014 2:05 PM
  • True. Well, I could if the client was single-threaded (then the server would just need to send stuff in order, and it would be guaranteed to get processed in order). With a multi-threaded client, I need to either use the ack and get the delay time, or to add extra intelligence in the client to handle out-of-order packets.
    Wednesday, January 08, 2014 2:19 PM