Labels

Thursday, September 23, 2010

Concurrency Problems - Scenarios


Before reading this post, recommended would be read these two post  - InstancingMode & ConcurrencyMode first.
These two post discuss wbout various scenarios lying under different variants of each mode.
But here in this post, I tried discussing only those that are having the Concurrency Problems.

InstancingMode.PerCall – Has Concurrency Problems.

Ideally in PerCall - Concurrency is not a concern. This is true with any Concurreny mode – Single/Renetrant/Multiple - Since new service object is assigned for each call.
However even being Per Call, this does not preclude them from sharing Business Objects or Cached Data across service operations.
These scenarios have the possibility of Concurrency.

PerCall – Sharing Cache Object
PerCall – Sharing Business/Data Object
PerCall – Sharing Business/Data Object



Note: Here all three scenarios have the 'Synchronization Problem'.

Solution:
·         The Concurrency problem can be avoided by having  'Manual Synchronization' in place.
·          Implementing manual synchronization will make sure that, if one request is accessing the shared 'Cache/Business/Data' object, no other request will be having the access at the same time.
·         Thus avoiding Concurrency problem.

PerCall – Sharing Cache Object –
Manual Synchronization
PerCall – Sharing Business/Data Object –
Manual Synchronization
PerCall – Sharing Business/Data Object –
Manual Synchronization



Note: Here in all three scenarios 'Manual Synchronization' is applied to avoid 'Synchronization Problem'.


InstancingMode.PerSession – Here we'll cover only Single & Multiple Thread Concurrency mode.

PerSession – Single Thread – Has Concurrency Problems

·         In case of Single Thread – there are three cases.
o    Shared State Level at Service Object (Fig 1)– At this level - there will be no 'Synchronization Problems' as only Single thread has access at a time. Thus no 'Manual Syncronization' will be required.
o    Separate Business Objects – Per Service Instance (Fig2) - At this level also - there will be no 'Synchronization Problems' as only Single thread has access at a time. Thus no 'Manual Syncronization' will be required.
o    Common Business Objects – All Service Instances (Fig 3)– Here there will be 'Synchronization Problems'. Thus here the 'Manual Syncronization' will be required.


Fig 1: PerSession – Single Thread – No Concurrency Issue
Fig 2: PerSession – Single Thread – Has Concurrency Issue




Solution: For Fig2.

·         The Concurrency problem can be avoided by having  'Manual Synchronization' in place.
·          Implementing manual synchronization will make sure that, if one request is accessing the shared 'Cache/Business/Data' object, no other request will be having the access at the same time.
·         Thus avoiding Concurrency problem.


Soution for Fig3 – Using Manual Synchronization




PerSession – Multiple Thread - Has Concurrency Problems

In case of Multiple Thread – There will be the 'Synchronization Problems'. Reason -  Multiple thread are allowed to access at same time.
The soultion would be to apply 'Manual Synchronization'.

PerSession – Multi Thread - Manual Synchronization
PerSession – Multi Thread - Manual Synchronization

Here: 'Manual Synchronization' is applied to avoid 'Synchronization Problem'.


InstancingMode.Singleton: Here we'll cover only Single & Multiple Thread Concurrency mode.

Singleton – Single Thread – Has no Concurrency Problem

·         In case of Single Thread – there will be no 'Synchronization Problems'. Since only one thread is allowed to access.
·         Thus no 'Manual Synchronization' is required.

Singleton – Single Thread – No Concurrency Issue
Singleton – Single Thread – No Concurrency Issue
Singleton – Single Thread – No Concurrency Issue


Note: Here in all three scenarios no 'Manual Synchronization' is applied. Sinceonly one thread is allowed to access. Thus inherntily there will be no 'Synchronization Problems'.

Singleton – Multiple Thread - Has Concurrency Problems

In case of Multiple Thread – There will be the 'Synchronization Problems'. Reason -  multiple thread are allowed to access at same time.
The soultion would be to apply 'Manual Synchronization'.

Solution:

·         The Concurrency problem can be avoided by having  'Manual Synchronization' in place.
·          Implementing manual synchronization will make sure that, if one request is accessing the shared 'Cache/Business/Data' object, no other request will be having the access at the same time.
·         Thus avoiding Concurrency problem.


Singleton – Multi Thread - Manual Synchronization
Singleton – Multi Thread - Manual Synchronization


Here: 'Manual Synchronization' is applied to avoid 'Synchronization Problem'.

Hope this helps.
 Thanks & Regards,
Arun Manglick

Tuesday, September 21, 2010

Instancing - InstanceContextMode

Instancing

·         Controls the way the Service Object are allocated to the Request Object.

·         Three Modes
·         InstanceContextMode.PerCall
·         InstanceContextMode.PerSession (Default)
·         InstanceContextMode.Single

InstanceContextMode.PerCall

·         A new Service Instance is created for each call, even if the same client issues the call.






Fig:1


·         No State is mainitained between calls.
·         Concurrency is not concenred - Since new service object is assigned for each call.
·         Concurrency is concenred -  For resources like Cache, shared by each Service Object, still requires protection for Concurrent access. See Below.

Look at below figures.

Service Operations still construct the Business & Data Access Objects to complete their work.
These Business & Data Access Objects, usually belongs to each Service object. Thus released with the completion of each service operation. (Fig 2)
Here even being Per Call, this does not preclude from sharing business objects or cached data across service operations. (Fig 3 & 4)
These scenarios have the possibility of Concurrency.

Fig:2 Seperate Business Objects for each Service Object –
No Concurrency Issue
Fig:3 Sharing Business Objects for each Service Object - Concurrency Issue
Fig:4 Sharing Cached between Service Objects
- Concurrency Issue




InstanceContextMode.PerSession

·         A new Service Instance is created for each client. i.e.
·         A new Service Instance is created for the first call from the client proxy, and kept alive for subsequent calls from the same client proxy.

Note: If the client is inactive beyond the Session Timeouts (Default – 10 mins), the Service Instance is disposed.


Fig:5



Look at below figures.

Maintain State at Business Object - Since each client call gets the same Service Instance for the session duration, the Service Instance can hold references to State-Aware Business Objects. (Fig 6)
Maintain State at Service Object – Here the idea is to maintain State Information in the Service Object, while allowing request to construct its own business object. (Fig 7).

Fig:6 State Aware Business Objects
Fig:7 State Un-Aware Business Objects



InstanceContextMode.Single

·         A new Service Instance is created and used for all the calls from all the clients.


Fig:8 Business Object shared by all request


            Since a singleton service object is shared across all the client request, it is really upto the Singleton to determine how its state is managed across those requests.
            By definition any references to business objects held by the singleton will be shared by all requests. (Fig 8)
           
Look at below figures.

Per Session Business Object – Fig 9

·         If the binding supports sessions and the 'Session Mode = Required/Allowed', a session is created for each client.
·         In this case. though the same Singleton object handles each request, a session identifier is assigned for each client to distinguish them from each other.
·         It is up to you to use this session identifier to allocate state to each client.

                        e.g.



[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SingletonService : ISingletonService, IDisposable
{
            Dictionary<string, int> m_counters = new Dictionary<string, int>();

            public void IncrementCounter()
            {
                        if (!m_counters.ContainsKey(OperationContext.Current.SessionId))
                                    m_counters.Add(OperationContext.Current.SessionId, 0);

                        m_counters[OperationContext.Current.SessionId]++;
            }
}

Ignoring State – Fig 10

·         Singleton scenario can also choose not to track sessions.
·         In this case, though all the requests will funnel thru the Singleton, each operation will constuct its own business objects.
·         In some aspects this removes the value of having a Singelton. So it is an unlikely scenario – As Singletons are usually chosen because there is a need to share some state between clients.

Fig:9 Per Session in Singleton
Fig:10 Ignoring State in Singleton – Unlikely Scenario


Thanks & Regards,
Arun Manglick

Concurrency -ConcurrencyMode

Concurrency:

This issue arrises when multiple request attempts to access the same Service object at runtime. i.e. (PerSession / Singleton Instancing Mode).
For PerCall there will be no concurrency issue, as a new Service object is instantiated for every request.
However PerSession/Singleton Services are at concurrency risk candidates.

Three Modes:

·         ConcurrencyMode.Single (Default) – A single thread has access to a Service Object at a given time.
·         ConcurrencyMode.Reentrant –
·         A single thread has access to a Service Object at a given time.
·         But the thread can exit the Service and can reenter without deadlock.

·         ConcurrencyMode.Multiple –
·         Multiple request threads have access to the service object.
·         Shared resources must be manually protected from concurrent access.

ConcurrencyMode.Single with PerSession/Singleton

·         In this case, a lock is acquired for the Service Object, while a request is being processed by that object.
·         Other calls to the same Service Object are blocked & queued.
·         When the request having the lock has completed, the lock is released. Then the next request in the queue acquires the lock and starts processing.

Fig:1 –Single - PerCall
Fig:2 – Single - PerSession
Fig:3 – Single - Singleton



ConcurrencyMode.Reentrant

Reentrant mode behaves similar to Single Mode. i.e. does not allows concurrent calls from clients.
However, Renentrant mode is required when a Service issues Callbacks (Two-Way). Single mode cannot handle these callbacks. (Fig 4)





Reentrant -PerCall Fig 5·         With Reentrant, when client callback is made (Label 2), the lock on the service instance is released so that the another call (Label 3), is allowed to acquire it. Fig 5.
·         When the outgoing call is returned (Fig5. - Label 3),it is queued to acquire the lock to complete its work.

This is not possible with Single-PerCall– In Single mode, deadlock is guaranteed, where as in Reentrant mode, the callback (Fig5. - Label 3) simply reacquires the lock.e.g. Compare Figure 4 & 5

Reentrant -PerSession
Fig  6·         Reentrant – PerSession gives more thruput as compared 'Single – PerSession' (Fig 2).
·         Reason being while the callback is in progress, another call T2 (labelled 2) from the client can acquire the lock, as the first thread(T1) has released it issue a callback.
·         T1 queued up on return, waiting for T2 to complete the work.

Reentrant - Singleton

Fig 7
·         Reentrant – Singleton gives more thruput as compared 'Single – Singleton'(Fig 3).
·         Reason being while the callback is in progress, another call from any client (T2/T3/T4) can acquire the lock, as the first thread(T1) has released it issue a callback.
·         Here for e.g. T3 has acquired the lock as the first thread(T1) has released it issue a callback.
·         T1 then later queued up on return, waiting for T3 to complete the work



Fig 4 – Single – PerCall with Callback
Fig:5 –Reentrant – PerCall
Fig:6 – Reentrant - PerSession
Fig:7 – Reentrant - Singleton


Reentrant – Can highly affect the 'Integrity'

·         Renetrancy can highly affect the state of object in case of PerSession & Singleton – Figure 6,7.
·         Reason being – A new thread (T2) will be using the same service object, while another thread T1 may have not compeleted it's work (busy with callback).
·         Thus make sure that the thread's state is always persisted safely with a resource manager, such as DB before releasing the lock for callback.This same persisted state could be retrieved  upon the callback's return, if applicable.

ConcurrencyMode.Multiple

Multiple mode provides high thruput, as the multiple threads are allowed to access to the same/shared Service object.
In this case, no locks are acquired on the Service object. All Shared State & Resources must be protected with Manual Synchronization Techniques.

In case of PerSession & Singleton, the 'State' is shared in the Service Instance.
Now here there could be two possiblilites.

·         If the State, itself references threadsafe objects, that encapsulate their own concurrency protection, then the first thread (T1) to access the State obejct, will acquire the lock for that State object (Fig 9,10). The other threads (T2 will queue, if it tries to access the State object.
·         If not, Service object should implement sycnhronization locks, before accessing those resources.


Fig:8 – Multiple - PerCall
Fig:9 – Multiple - PerSession
Fig:10 – Multiple - Singleton

Note: In case of Multiple – No thread are blocked (See Green cricles). i.e this is the case where the maximum concurrency attention is required.




Multiple -PerCall Fig 8
PerCall, are not immune to synchronization issues. Reason being, there is no possiblity of  'Shared State' in the service instance.
However, individual Service Instances, may access a 'Shared' resource. The shared resource could be:

·         Global Object – Relies on 'Manual Synchronization Techniques'
·         Shared Cache – Relies on 'Manual Synchronization Techniques'
·         Database – It relies on 'Resource Managers' & 'Transactions' to manage consistency & concurrency.

Here as shown in fig(8), manulaly locks have been applied on the Cache object. Thus once T1 acquires the lock, another thread T2 will wait. Thus avoiding Concurrency issue.

Multiple -PerSession
Fig  9PerSession, could also have the 'PerCall' scenario (Shared Cache).
However, additionally, they are also likely to have 'Shared State' in the Service Instance.

Thus here one should implement the Manual/Custom Synchronziation to control the access to the 'Shared State' object.. The synchronization should
work in a way that if T1 has lock on that state object and if another thread(T2) tries access the state, then T2 should be queued.
Multiple - Singleton

Fig 10
Same as 'PerSession'.

Additionally, it is entirely possible, that multiple types of 'Shared State' exist in the same 'Service Instance' to increase the throughput.
In such cases, different locks should be acquired for relevant type of 'State' object.
This way, multiple thread T1& T3 can complete work concurrently, since they don't share state. T2 & T4 will queue, waiting for the respectives locks to free.


Manual/Custom Synchronization:

There are many ways to implement 'Custom' Syynchronization. Few are:

·         Monitor
·         Mutex
·         Semaphore
·         ReadWriterLock
·         Interlocked
·         .Net Attributes – Used to synchronize access to a type.



[ServiceContract(Namespace = "http://www.ArunM.com/samples/2006/06")]
    public interface IMessagingService
    {
        [OperationContract(IsOneWay=true)]
        void SendMessage1(string message);

        [OperationContract(IsOneWay=true)]
        void SendMessage2(string message);
   
        [OperationContract(IsOneWay=true)]
        void SendMessage3(string message);
    }


[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,
                 ConcurrencyMode=ConcurrencyMode.Multiple,
                                       UseSynchronizationContext=false)]
    public class MessagingService : IMessagingService
    {
        int m_messageCounter;
        Dictionary<int, string> m_messages = new Dictionary<int, string>();
        Mutex m_mutex = new Mutex(false);

        int m_messageCounter2;
        Dictionary<int, string> m_messages2 = new Dictionary<int, string>();
        Mutex m_mutex2 = new Mutex(false);

        #region IMessagingService Members

        public void SendMessage1(string message)
        {
            try
            {
                Monitor.Enter(this);

                m_messages.Add(m_messageCounter, message);
                Trace.WriteLine(string.Format("Message {0}: {1}", m_messageCounter, message));
                m_messageCounter++;
            }
            finally
            {
                Monitor.Exit(this);
            }

        }

        public void SendMessage2(string message)
        {
           lock(this)
           {
             m_messages.Add(m_messageCounter, message);
             Trace.WriteLine(string.Format("Message {0}: {1}", m_messageCounter, message));
             m_messageCounter++;
           }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void SendMessage3(string message)
        {
           m_messages2.Add(m_messageCounter2, message);
           Trace.WriteLine(string.Format("Message {0}: {1}", m_messageCounter2, message));
           m_messageCounter2++;
        }
       



Hope this helps.

Thanks & Regards,
Arun Manglick