locked
Question about concurrency and sessions RRS feed

  • Question

  • Hello.

    I've been trying to understand the interdependencies between: SessionMode, InstanceContextMode, ConcurrencyMode and the used binding.

    I’ve done a systematic test where I issue two concurrent asynchronous calls over the same channel, and observed the following:

    1. With SessionMode=NotAllowed the calls are processed concurrently, independently of the InstanceContextMode (PerCall or PerSession), of the ConcurrencyMode and of the binding (NetTcpBinding throws an exception). This is ok because every call is handled by a different instance.
    2. With SessionMode=Required and InstanceContextMode=PerSession and ConcurrencyMode=Single, the calls are processed sequentially. This is ok because the instance is always the same and the concurrency mode is single
    3. With SessionMode=Required and InstanceContextMode=PerCall and ConcurrencyMode=Single, the calls are processed sequentially. This is strange, since every call is handled by a different instance.
    4. With SessionMode=Required and ConcurrencyMode=Multiple (InstanceContextMode = PerCall or PerSession), the behavior depends on the binding:
                a) WSHttpBinding: calls processed sequentially. This is strange, since the concurrency mode is multiple.
                b) NetTcpBinding: calls processed concurrently: This is ok.

    Is the behavior for items 3 and 4 a) correct? What is the justification for this behavior.

    I’ve made the same tests with two different channel instances and the processing is always concurrent. This is ok, since the sessions are always different, which implies that the instances are always different.

    So, it appears that with SessionMode=Required

    • ConcurrencyMode = Single implies sequential processing on the same session.
    • Using WSHttpBinding implies sequential processing on the same session, independently of the ConcurrencyMode.

    Are these conclusions correct? What is the explanation for this behavior?

    These tests were done with the RTM version (I can send the code if needed).

    Best regards
    Pedro Felix
    Friday, November 10, 2006 5:31 PM

Answers

  • Hi

     

    Sorry for the delay in response.

     

    The behavior you see in the WSHttpBinding case is due to an  RM implementaion detail. The RM transmission window starts at a size of 1 message. It grows over time as acknowledgements return. What you are probably doing is blocking your service method. If you block your service method, you block the acknowledgements and the window never grows. Thus you dont see the concurrent behavior.

     

    You could fix this by sending a bunch of non-blocking messages (say 5 to 10). And then try your concurrency scenario. You would see that the calls are concurrent now. This implementation detail exists only with WSHttpBinding since this binding ties acknowledgements to the application response.

     

    Alternatively, you could use WSHttpBinding with Security turned on and RM turned off.

     

    Thanks

    -ram

     

    Wednesday, October 10, 2007 12:55 AM

All replies

  • First, when you say "asynchronous" calls do you mean IsOneWay calls or AsyncPattern calls? If the latter, on which side: Client or service?

    As to the WSHttpBinding stuff you have to realize that http is by definition two-way; services do not fire off the http response (empty, in the case of one-way) until processing has finished on the service. This holds up the channel instance. This is an implementation detail of that binding, which is why tcp doesn't work that way -- the session is a different type of session. It would work the way you expect, too, if you had a UDP channel.

     

    Friday, November 10, 2006 9:25 PM
  • Hello. Thanks for the reply.
    1. When I say asynchronous I was refering to AsyncPattern calls on the client side. The operation at the service is a normal synchronous operation (request-reply). I'm assuming that the client channel supports multiple concurrent requests in the same session with both WSHttpBinding and NetTcpBinding. Am I correct?

    2. I don't understand why there cannot be concurrent processing when the binding is WSHttpBinding and sessions are required. I understand that the session implementation is diferent: TCP already has sessions and WSHttpBinding uses WS-ReliableMessaging or WS-SecureConversation for that purpose. What I don't understand is why  the second implementation doesn't allow concurrent processing in the same session.

    3. I don't understand why binding=NetTcpBinding, InstanceContextMode=PerCall and ConcurrencyMode=Single implies sequential processing in the same session. Since InstanceContextMode=PerCall, the service instances are diferent, so there could be concurrent processing

    What am I missing here?

    Once again, thanks for the reply
    Best regards
    Pedro Felix
    Friday, November 10, 2006 10:53 PM
  • I think your results sound not quite right to me... I am curious, are you explicitly calling Open() on the client channel before starting the concurrent calls?  If you don't call Open() explicitly and start making concurrent calls, the 'auto-Open' feature of WCF client channels sometimes sequentializes calls while the auto-Open is still processing, and I wonder if this is interacting with your results.
    Monday, November 13, 2006 8:23 PM
  • Hello.
    Yes, I didn't open the channels explicitly. Due to this, the synchronous operations were always processed sequentially. That was the reason why I did these tests using the client async operations.
    I've rerun my tests using a synchronous operation (non OneWay), with an explicit open, and observed the following (the '<<<' marks the "strange" cases):
    NotAllowed PerCall Single BasicHttpBinding => Concurrent
    NotAllowed PerCall Single NetTcpBinding => exception thrown
    NotAllowed PerCall Single WSHttpBinding => Concurrent
    NotAllowed PerCall Multiple BasicHttpBinding => Concurrent
    NotAllowed PerCall Multiple NetTcpBinding => exception thrown
    NotAllowed PerCall Multiple WSHttpBinding => Concurrent
    NotAllowed PerSession Single BasicHttpBinding => Concurrent
    NotAllowed PerSession Single NetTcpBinding => exception thrown
    NotAllowed PerSession Single WSHttpBinding => Concurrent
    NotAllowed PerSession Multiple BasicHttpBinding => Concurrent
    NotAllowed PerSession Multiple NetTcpBinding => exception thrown
    NotAllowed PerSession Multiple WSHttpBinding => Concurrent
    Required PerCall Single BasicHttpBinding => exception thrown
    Required PerCall Single NetTcpBinding => Sequential  <<<
    Required PerCall Single WSHttpBinding => Sequential <<<
    Required PerCall Multiple BasicHttpBinding => exception thrown
    Required PerCall Multiple NetTcpBinding => Concurrent
    Required PerCall Multiple WSHttpBinding => Sequential <<<
    Required PerSession Single BasicHttpBinding => exception thrown
    Required PerSession Single NetTcpBinding => Sequential
    Required PerSession Single WSHttpBinding => Sequential
    Required PerSession Multiple BasicHttpBinding => exception thrown
    Required PerSession Multiple NetTcpBinding => Concurrent
    Required PerSession Multiple WSHttpBinding => Sequential <<<

    Thanks for your help
    Best regards
    Pedro Felix
    Monday, November 13, 2006 11:18 PM
  • I've made a little block schema.
    Tell me if what i'm writing there is correct and if not, how would you modify it?
    I'm assuming certain things undocumented which should be obvious... i don't know...

    I havn't done any test, i've just read documentation. And i'm not taking into account
    the session mode.

    Bye.
    Sunday, November 19, 2006 2:26 PM
  •  Pedro Felix wrote:
    Hello.
    Yes, I didn't open the channels explicitly. Due to this, the synchronous operations were always processed sequentially. That was the reason why I did these tests using the client async operations.
    I've rerun my tests using a synchronous operation (non OneWay), with an explicit open, and observed the following (the '<<<' marks the "strange" cases):
    NotAllowed PerCall Single BasicHttpBinding => Concurrent
    NotAllowed PerCall Single NetTcpBinding => exception thrown
    NotAllowed PerCall Single WSHttpBinding => Concurrent
    NotAllowed PerCall Multiple BasicHttpBinding => Concurrent
    NotAllowed PerCall Multiple NetTcpBinding => exception thrown
    NotAllowed PerCall Multiple WSHttpBinding => Concurrent
    NotAllowed PerSession Single BasicHttpBinding => Concurrent
    NotAllowed PerSession Single NetTcpBinding => exception thrown
    NotAllowed PerSession Single WSHttpBinding => Concurrent
    NotAllowed PerSession Multiple BasicHttpBinding => Concurrent
    NotAllowed PerSession Multiple NetTcpBinding => exception thrown
    NotAllowed PerSession Multiple WSHttpBinding => Concurrent
    Required PerCall Single BasicHttpBinding => exception thrown
    Required PerCall Single NetTcpBinding => Sequential <<<
    Required PerCall Single WSHttpBinding => Sequential <<<
    Required PerCall Multiple BasicHttpBinding => exception thrown
    Required PerCall Multiple NetTcpBinding => Concurrent
    Required PerCall Multiple WSHttpBinding => Sequential <<<
    Required PerSession Single BasicHttpBinding => exception thrown
    Required PerSession Single NetTcpBinding => Sequential
    Required PerSession Single WSHttpBinding => Sequential
    Required PerSession Multiple BasicHttpBinding => exception thrown
    Required PerSession Multiple NetTcpBinding => Concurrent
    Required PerSession Multiple WSHttpBinding => Sequential <<<

    Thanks for your help
    Best regards
    Pedro Felix


    I think there is something wrong in your tests.

    i.e.:

    1-Required PerCall Single NetTcpBinding => Sequential <<<
    2-Required PerCall Multiple NetTcpBinding => Concurrent

    This cannot happen, concurrency mode is not meaningful when you set PerCall so
    behaviour cannot change from 1 to 2. If this change is true, the documentation is wrong.
    Pease post your code.

    Bye
    .
    Sunday, November 19, 2006 2:53 PM
  • Hello:

    Yes, I also think that there is something strange with my tests. I just cannot figure out what.
    I'm sending the code attached to this post.

    Pedro Felix

    ---------------------------------------------

    namespace Concurrency
    {
        [ServiceContract]
        public interface IService
        {
            [OperationContract]
            string Operation1(string s);

        }

        [ServiceContract]
        public interface IAsyncService
        {
            [OperationContract(Action = "http://tempuri.org/IService/Operation1", ReplyAction = "http://tempuri.org/IService/Operation1Response")]
            string Operation1(string s);

            [OperationContract(AsyncPattern = true, Action = "http://tempuri.org/IService/Operation1", ReplyAction = "http://tempuri.org/IService/Operation1Response")]
            System.IAsyncResult BeginOperation1(string s, System.AsyncCallback callback, object asyncState);

            string EndOperation1(System.IAsyncResult result);
        }

        //-- Implementation
        [ServiceBehavior]
        class ServiceImpl : IService
        {
            public static volatile bool begin = false;
            public static volatile bool end = false;

            
            public string Operation1(string s)
            {
                if(begin == true){
                    if(end == true) Console.WriteLine("Sequential");
                    else Console.WriteLine("Concurrent");
                }
                begin = true;
                Thread.Sleep(4000);
                end = true;
                return s.ToUpper();
            }
        }

        delegate void ServiceCaller<CT>(CT channel, string s);

        class Program
        {
            static void
                RunTestCase<CT>(SessionMode sm, InstanceContextMode icm, ConcurrencyMode cm, Binding b, string address, ServiceCaller<CT> action)
            {
                Console.Write("{0} {1} {2} {3} => ",
                    sm, icm, cm, b.GetType().Name);
                ServiceHost sh = new ServiceHost(typeof(ServiceImpl), new Uri(address));
                sh.AddServiceEndpoint(typeof(IService), b, "ep1");
                sh.Description.Endpoints[0].Contract.SessionMode = sm;
                ServiceBehaviorAttribute sb = sh.Description.Behaviors.Find<ServiceBehaviorAttribute>();
                if (sb == null)
                {
                    sb = new ServiceBehaviorAttribute();
                }
                sb.InstanceContextMode = icm;
                sb.ConcurrencyMode = cm;
                try
                {
                    ServiceImpl.begin = false;
                    ServiceImpl.end = false;

                    sh.Open();

                    ChannelFactory<CT> cf = new ChannelFactory<CT>(b, address + "/ep1");
                    cf.Endpoint.Contract.SessionMode = sm;
                    CT[] ch = new CT[2];
                    ch[0] = cf.CreateChannel();
                    ch[1] = cf.CreateChannel();
                    ICommunicationObject ico = ch[0] as ICommunicationObject;
                    ico.Open();
                    ico = ch[1] as ICommunicationObject;
                    ico.Open();

                    Semaphore sem = new Semaphore(0, 2);
                    for (int i = 0; i < 2; ++i)
                    {
                        int ix = i;
                        ThreadPool.QueueUserWorkItem(
                            delegate{
                                action(ch[0], Thread.CurrentThread.ManagedThreadId.ToString());
                                sem.Release();
                            });
                    }
                    sem.WaitOne();
                    sem.WaitOne();
                    (ch[0] as IClientChannel).Close();
                    (ch[1] as IClientChannel).Close();

                    sh.Close();

                }catch(Exception exc){
                    Console.WriteLine("exception thrown ", exc.Message);
                    if(sh.State == CommunicationState.Opened) sh.Close();
                }
            }

            static void Main(string[] args)
            {
                SessionMode[] sma = { SessionMode.NotAllowed, SessionMode.Required };
                InstanceContextMode[] icma = { InstanceContextMode.PerCall, InstanceContextMode.PerSession };
                ConcurrencyMode[] cma = {ConcurrencyMode.Single, ConcurrencyMode.Multiple};
                Binding[] ba = {
                    new BasicHttpBinding(),
                    new NetTcpBinding(),
                    //new WSHttpBinding(SecurityMode.None,false),
                    new WSHttpBinding(SecurityMode.None,true)
                  
                };
                string[] aa = {
                    "http://localhost:8080/concurrency",
                    "net.tcp://localhost:8080/concurrency",
                    //"http://localhost:8080/concurrency",
                    "http://localhost:8088/concurrency"                
                };

                foreach (SessionMode sm in sma)
                {
                    foreach (InstanceContextMode icm in icma)
                    {
                        foreach(ConcurrencyMode cm in cma){
                        for (int i = 0; i < ba.Length; ++i)
                        {
                            
                            RunTestCase<IService>(sm, icm, cm, baIdea, aaIdea,
                                delegate(IService ch, string s) { ch.Operation1(s); }
                            );
                             
                            /*
                            RunTestCase<IAsyncService>(sm, icm, cm, baIdea, aaIdea,
                                delegate(IAsyncService ch, string s)
                                {
                                    IAsyncResult ar = ch.BeginOperation1(s, null, null);
                                    ch.EndOperation1(ar);
                                }
                            );
                             */
                        }
                        }
                    }
                }
                Console.WriteLine("press any key to exit");
                Console.ReadKey();
            }
        }
    }

    Monday, November 20, 2006 10:16 PM
  •  

    Hello Felix.

    I'm going into the same question. Why on sessionful channel, and options like Required, PerCall, Single new session wouldn't create for each call ?

    Do you have an answer on your questions (and possibly on my) ?

    Wednesday, September 26, 2007 11:52 AM
  • Hi Felix,

     

    I tried the following scenario of configuring a service with InstanceContextMode.PerCall and implementing a sessionful contract with SessionMode.Required and I see that each call results in a new instance of the service being created, which looks like the expected behavior.

     

    Do you see a different behavior?

     

    Thanks

     

     

     

    Thursday, September 27, 2007 8:20 AM
  • Hi Ram

    The msdn says that on sessionful channel and InstanceContextMode.PerCall a new session will be created for each incoming call, but not only a new service instance.

    IMHO,  Felix has mentioned that this behaviour is specific for different binding.
    Thursday, September 27, 2007 8:30 AM
  • Hello

    It has been a while since I've though on those problems. From what I can recall, the problem was not on the instancing behavior but on the concurrency behavior. I've updated my code to show if the service is using the same instance or different instances. The results were:

     

    NotAllowed PerCall Single BasicHttpBinding => Different instance, Concurrent
    NotAllowed PerCall Single NetTcpBinding => exception thrown
    NotAllowed PerCall Single WSHttpBinding => Different instance, Concurrent
    NotAllowed PerCall Multiple BasicHttpBinding => Different instance, Concurrent
    NotAllowed PerCall Multiple NetTcpBinding => exception thrown
    NotAllowed PerCall Multiple WSHttpBinding => Different instance, Concurrent
    NotAllowed PerSession Single BasicHttpBinding => Different instance, Concurrent
    NotAllowed PerSession Single NetTcpBinding => exception thrown
    NotAllowed PerSession Single WSHttpBinding => Different instance, Concurrent
    NotAllowed PerSession Multiple BasicHttpBinding => Different instance, Concurrent
    NotAllowed PerSession Multiple NetTcpBinding => exception thrown
    NotAllowed PerSession Multiple WSHttpBinding => Different instance, Concurrent
    Required PerCall Single BasicHttpBinding => exception thrown
    Required PerCall Single NetTcpBinding => Different instance, Sequential a)
    Required PerCall Single WSHttpBinding => Different instance, Sequential b)
    Required PerCall Multiple BasicHttpBinding => exception thrown
    Required PerCall Multiple NetTcpBinding => Different instance, Concurrent
    Required PerCall Multiple WSHttpBinding => Different instance, Sequential c)
    Required PerSession Single BasicHttpBinding => exception thrown
    Required PerSession Single NetTcpBinding => Same instance, Sequential
    Required PerSession Single WSHttpBinding => Same instance, Sequential

    Required PerSession Multiple BasicHttpBinding => exception thrown
    Required PerSession Multiple NetTcpBinding => Same instance, Concurrent
    Required PerSession Multiple WSHttpBinding => Same instance, Sequential d)

     

    The red lines correspond to unexpected (at least to me) behavior:

     

    1) The instancing behavior seems to be always ok: "same instance" only happens when sessions are "Required" and InstanceContextMode is "PerSession".

     

    2) The concurrency does not seem to be according to the specs, namely:

    cenario a): the behavior should be concurrent, since the instances are different.

     

    cenario b): as above

     

    cenario c): the behavior should be concurrent, since the instances are different. Notice that the same scenario with NetTcpBindigs results in the expected concurrent behavior

     

    cenario d): as above

     

    Perhaps there is a bug hidden in my test code Sad

     

    Pedro Felix 

    Thursday, September 27, 2007 10:33 AM
  • Hi Felix

    IMHO you should put an additional column after "=>". That is sessionId (on sessionful channel).

    And you'll see that b) scenario has same sessions for two call (although PerCall is used). I think that is reason of the same thread used.

    And Felix, could you answer a question about a Callback scenario ?

    If you have a time check this.

    I have a service that register incoming client request and save OperationContext into some storage.

    Also the service raises events to all registered clients.

    Problem has occurs then client has gone (restarted)

    The Service still have abandoned client a try to send a message.

    How you'll solve the problem ?

    Friday, September 28, 2007 5:33 PM
  • Hi

     

    With SessionMode.Required, and InstanceContextMode.PerCall, even though a new service instance is used for each call, all calls made on the same sessionful channel would use the same session.

     

    Also, the ConcurrencyMode, in addition to being related to the InstanceContextMode, is also tied to the session. So, basically a ConcurrencyMode.Single would imply that all calls belonging to a particular session would be sequential even if they are processed by different instances.

     

    So, in the above scenarios, (A) and (B) being sequential is expected.


    For scenarios (C) and (D), I tried a simplified scenarios and was able to get concurrent processing. Could it be possible that your test code is doing something that causes the sequential behavior?

     

    Our apologies if the msdn documents are not clear about this. We will get them updated.

     

    Thanks

    Tuesday, October 2, 2007 5:40 AM
  • Hello

    Thanks for the response.

    If the ConcurrencyMode is also "tied to the session", and not only to the instance, then I fully understand scenarios (A)  and (B).

    Regarding scenarios (C) and (D) and my test code:
    1. I'm using WSHttpBinding with SecurityMode.None and reliable session enable
    2. On the client side:
      1. I create a single ChannelFactory, passing the binding and the address
      2. I use the ChannelFactory to create to channel instances
      3. I open the two channel instances
      4. I call the service operation methods concurrently on two thread from the thread pool (using QueueUserWorkItem)
    3. On the service side, I just observe if the operations are executes sequentially or concurrently
    I've posted the code on a previous post.
    What puzzles me is that, on the two last scenarios, with NetTcpBinding the behavior is concurrent but with WsHttpBinding the behavior is sequential.

    Thanks for the clarification regarding the connection between ConcurrencyMode and sessions.

    Pedro Felix

    Tuesday, October 2, 2007 10:13 AM
  • Hi

     

    Sorry for the delay in response.

     

    The behavior you see in the WSHttpBinding case is due to an  RM implementaion detail. The RM transmission window starts at a size of 1 message. It grows over time as acknowledgements return. What you are probably doing is blocking your service method. If you block your service method, you block the acknowledgements and the window never grows. Thus you dont see the concurrent behavior.

     

    You could fix this by sending a bunch of non-blocking messages (say 5 to 10). And then try your concurrency scenario. You would see that the calls are concurrent now. This implementation detail exists only with WSHttpBinding since this binding ties acknowledgements to the application response.

     

    Alternatively, you could use WSHttpBinding with Security turned on and RM turned off.

     

    Thanks

    -ram

     

    Wednesday, October 10, 2007 12:55 AM
  • Hello

    Thanks for your reply.

    1) I don't have a real application that needs to be fixed. I've done these experiences a couple of months ago when was I was studying the concurrency+session+instance behaviors and parameterizations of WCF. Since this subject wasn't clear to me, I created a little test scenario to observe the behaviors for all the different parameter combinations.

    2) Regarding your explanation of the WSHttpBinding: what is the scope of the "RM transmission window"? Is this per service or per session. Notice that I'm using the same channel factory but different channels to perform the concurrent calls. Is there a diferent session for each channel, or the same session is used for every channel created by the same channel factory?

    Thanks for your very informative reply.

    Pedro Felix
    Wednesday, October 10, 2007 11:10 AM
  • Hi

     

    There would be a different session for each channel, even though they are created by the same channel factory.

     

    Thanks

    Thursday, October 11, 2007 9:09 PM
  • Hello

    Once again, thanks for your response.
    Regarding your previous response about the WSHttpBinding and the windows size: what is the scope of this windows? Is it associated with a session or with an endpoint?

    Thanks
    Pedro Felix

    Thursday, October 11, 2007 9:14 PM
  • Hi Pedro

     

    The RM transmission window is associated with the session.

     

    I also tried the scenario across multiple sessions (different channels created from same channel factory) and was able to see concurrent processing without having to warm up the transmission window.

     

    Is this the scenario that you are referring to?

     

    Thanks

    ram

     

     

    Friday, October 12, 2007 7:20 AM
  • Dear All,
    I'm quite new to the WCF service and the one i want to know is , for nettcpbinding , does it  mean that the service will take the p2p connection with client forever(like socket programming) unless the client close the proxy, and if so, what would be the maximum concurrent connections of clients able to connect to nettcpbinding service at the same time? because my knowledge for the socket programming is , even the developer develop the sock programming so nice, the max concurrent connection is not more than 3000 connections. Please kindly advice.
    Thanks,
    Pwint

    Monday, November 28, 2011 9:11 AM