Wednesday, February 04, 2009

Instance Management in WCF

By and large, the service instance mode is strictly a service-side implementation detail that should not manifest itself on the client side in any way. To support that and a few other local service-side aspects, WCF defines the notion of behaviors. A behavior is a local attribute of a service that does not affect its communication patterns.

Three Instance Management mode is available.

1.Per-Call
2.Per-Session
3.Single

Other Instance Management techniques are

1.Demarcating Operations
2.Instance Deactivation
3.Throtlling


-------------------------
Per-Call
-------------------------
When the service type is configured for per-call activation, a service instance (the CLR object) exists only while a client call is in progress. Every client request
(that is, a method call on the WCF contract) gets a new dedicated service instance

Per-call services are the Windows Communication Foundation default instantiation mode

-------------------------
Benefits
-------------------------
Creating and destroying a service instance repeatedly on the service side without tearing down the connection to the client (with its client-side proxy) is a lot cheaper than creating an instance and a connection.

The second benefit is that forcing the service instance to reallocate or connect to its resources on every call caters very well to transactional resources and transactional programming, because it eases the task of enforcing consistency with the instance state.


The third benefit of per-call services is that they can be used in conjunction with queued disconnected calls because they allow easy mapping of service instances to
discrete queued messages.



[ServiceContract]
interface IMyContract {...}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyService : IMyContract {...}



If a per-call service must be state-aware, meaning that state from one call must persist to a future call, the implementation itself must proactively manage the state,giving the client the illusion of a continuous session.

An instance of a per-call service is created just before every method call and destroyed immediately after each call. Therefore, at the beginning of each call, the

object should initialize its state from values saved in some storage, and at the end of the call it should return its state to the storage. Such storage is typically
either a database or the file system, but it can also be volatile storage like static variables. However, not all of the object's state can be saved as-is.

For example, if the state contains a database connection, the object must reacquire the connection at construction or at the beginning of every call and dispose of the
connection at the end of the call or in its implementation of IDisposable.Dispose.

Using per-call instance mode has one important implication for operation design: every operation must include a parameter to identify the service instance whose state needs to be retrieved.

-------------------------
Per-Session
-------------------------


WCF can maintain a session between a client and a particular service instance. When the client creates a new proxy to a service configured as a sessionful service, the
client gets a new dedicated service instance that is independent of all other instances of the same service. That instance will remain in service usually until the client no longer needs it. This activation mode is very much like the classic client-server model. This mode is also sometimes referred to as private sessions. Each private session uniquely binds a proxy and its set of client- and service-side channels to a particular service instance.

Because the service instance remains in memory throughout the session, it can maintain state in memory, and the programming model is very much like that of the classic client/server. Consequently, it also suffers from the same scalability and transaction issues as the classic client/server model. A service configured for private sessions cannot typically support more than a few dozen (or perhaps up to a hundred or two) outstanding clients due to the cost associated with each such dedicated service instance.


The client session is per service endpoint per proxy. If the client creates another proxy to the same or a different endpoint, that second proxy will be associated
with a new instance and session.


[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
class MyService : IMyContract {...}


The session typically terminates when the client closes the proxy. This causes the proxy to notify the service that the session has ended.


In order to correlate all messages from a particular client to a particular instance, WCF needs to be able to identify the client. One way of doing that is to rely on a transport-level session; that is, a continuous connection at the transport level, such as the one maintained by the TCP and IPC protocols.

public enum SessionMode
{
Allowed,
Required,
NotAllowed
}

[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
[OperationContract]
void MyMethod( );
}



SessionMode.Allowed is the default value of the property.

The SessionMode.Required value mandates the use of a transport-level session, but not necessarily an application-level session. You cannot have a contract configured with SessionMode.Required with a service's endpoint whose binding does not maintain a transport-level session, and this constraint is verified at the service load
time.

SessionMode.NotAllowed disallows the use of a transport-level session, which precludes an application-level session. Regardless of the service configuration, it always behaves as a per-call service.


If a per-call service without a transport session (such as with the BasicHttpBinding or with SessionMode.NotAllowed) accesses the SessionId property, it will return
null, since there is no session and therefore no ID.


Typically, the session will end once the client closes the proxy. However, in case the client fails to terminate gracefully or encounters a communication problem, each
session also has an idle time timeout that defaults to 10 minutes—the session will automatically terminate after 10 minutes of inactivity from the client, even if the
client still intends to use the session. Once the session has terminated due to the idle timeout, if the client tries to use its proxy, the client will get a
CommunicationObjectFaultedException.


Both the client and the service can configure a different timeout by setting a different value in the binding. The bindings that support reliable transport-level session provide the ReliableSession property with the InactivityTimeout property used for configuring the idle timeout.


For example, the following shows the code that is required to programmatically configure an idle timeout of 25 minutes for the TCP binding:

NetTcpBinding tcpSessionBinding = new NetTcpBinding();
tcpSessionBinding.ReliableSession.Enabled = true;
tcpSessionBinding.ReliableSession.InactivityTimeout =
TimeSpan.FromMinutes(25);


Here is the equivalent configuration setting using a config file:

<netTcpBinding>
<binding name="TCPSession">
<reliableSession enabled="true" inactivityTimeout="00:25:00"/>
</binding>
</netTcpBinding>

If both the client and the service configure timeouts, then the shorter timeout prevails.

There is another esoteric service-side configuration for session termination. The ServiceBehavior attribute offers an advanced option for managing the session shutdown via the AutomaticSessionShutdown property. This property is intended for optimizing certain callback scenarios, and can be safely ignored in most cases. In a nutshell,AutomaticSessionShutdown defaults to TRue so that when the client closes the proxy, the session is terminated. Setting it to false causes the session to continue until the service explicitly closes its sending channel. When set to false, the client of a duplex session must manually close the output session on the duplex client channel, otherwise the client will hang waiting for the session to terminate.

-------------------------
Singleton Service
-------------------------

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class MyService : IMyContract {...}


The singleton service is the ultimate sharable service.

When a service is configured as a singleton, all clients independently get connected to the same single well-known instance, regardless of which endpoint of the
service they connect to.

The singleton service lives forever and is only disposed of once the host shuts down.

The singleton is created exactly once, when the host is created.

Using a singleton does not require the clients to maintain a session with the singleton instance, or to use a binding that supports a transport-level session.


Closing the client proxy will only terminate the session, not the singleton instance. In addition, the session will never expire. If the singleton service supports contracts without a session, those contracts will not be per-call: they too will be connected to the same instance. By its very nature, the singleton is shared, and each client should simply create its own proxies to it.



-------------------------
Demarcating Operations
-------------------------
Sometimes, a sessionful contract has an implied order to operation invocations. Some operations cannot be called first, while other operations must be called last. For
example, consider this contract used to manage customer orders:

[ServiceContract(SessionMode = SessionMode.Required)]
interface IOrderManager
{
[OperationContract]
void SetCustomerId(int customerId);

[OperationContract]
void AddItem(int itemId);

[OperationContract]
decimal GetTotal( );

[OperationContract]
bool ProcessOrders( );
}



The contract has the following constraints: the client must provide the customer ID as the first operation in the session, or else no other operations can take place;
items may be added, and the total calculated, in any order, and as often as the client wishes; processing the order terminates the session, and therefore must come
last.

WCF allows contract designers to designate contract operations as operations that can or cannot start or terminate the session using the IsInitiating and IsTerminating properties of the OperationContract attribute.



Going back to the order management contract, you can use demarcating operations to enforce the interaction constraints:

[ServiceContract(SessionMode = SessionMode.Required)]
interface IOrderManager
{
[OperationContract]
void SetCustomerId(int customerId);

[OperationContract(IsInitiating = false)]
void AddItem(int itemId);

[OperationContract(IsInitiating = false)]
decimal GetTotal( );

[OperationContract(IsInitiating = false,IsTerminating = true)]
bool ProcessOrders( );
}
//Client code
OrderManagerClient proxy = new OrderManagerClient( );

proxy.SetCustomerId(123);
proxy.AddItem(4);
proxy.AddItem(5);
proxy.AddItem(6);
proxy.ProcessOrders( );

proxy.Close( );


-------------------------
Instance Deactivation
-------------------------

The common way of controlling context deactivation is via the ReleaseInstanceMode property of the OperationBehavior attribute:

public enum ReleaseInstanceMode
{
None,
BeforeCall,
AfterCall,
BeforeAndAfterCall,
}



You typically apply instance deactivation only on some service methods, but not all of them, or with different values on different methods:

[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
[OperationContract]
void MyMethod( );

[OperationContract]
void MyOtherMethod( );
}
class MyService : IMyContract,IDisposable
{
[OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.AfterCall)]
public void MyMethod( )
{...}
public void MyOtherMethod( )
{...}
public void Dispose( )
{...}
}



The reason you typically apply it sporadically is that if you were to apply it uniformly you would have ended up with a per-call-like service, so you might as well
have configured the service as per-call. If relying on instance deactivation assumes a certain call order, you can try and enforce that order using demarcating
operations.


[ServiceContract(SessionMode = SessionMode.Required)]
interface IMyContract
{
[OperationContract]
void MyMethod( );
}
class MyService : IMyContract,IDisposable
{
public void MyMethod( )
{
//Do some work then
OperationContext.Current.InstanceContext.ReleaseServiceInstance( );
}
public void Dispose( )
{...}
}



Calling ReleaseServiceInstance( ) has a similar effect to using ReleaseInstanceMode.AfterCall. When used in a method decorated with ReleaseInstanceMode.BeforeCall it has a similar effect to using ReleaseInstanceMode.BeforeAndAfterCall.


-------------------------
Throttling
-------------------------



While it is not a direct instance management technique, throttling enables you to restrain client connections and the load they place on your service.

Throttling enables you to avoid maxing out your service and the underlying resources it allocates and uses. When throttling is engaged, if the settings you configure are exceeded, WCF will automatically place the pending callers in a queue and serve them out of the queue in order.

If the client's call timeout expires while pending in the queue, the client will get a TimeoutException. Throttling is done per service type; that is, it affects all instances of the service and all its endpoints. This is done by associating the throttle with every channel dispatcher the service uses.


Throttle Default Values

ConcurrentCalls = "16"
ConcurrentSessions = "10"
ConcurrentInstances = unlimited


Throttled Connections in the Binding
--------------------------------------
When you use the TCP and named-pipe bindings, you can also configure the maximum connection number for a particular endpoint in the binding itself. Both the

NetTcpBinding and the NetNamedPipeBinding offer the MaxConnections property:



On the host side, you can set that property either programmatically or by using a config file:

<bindings>
<netTcpBinding>
<binding name = "TCPThrottle" maxConnections = "25"/>
</netTcpBinding>
</bindings>



MaxConnections defaults to 10. When both a binding-level throttle and a service-behavior throttle sets the max connection value, WCF chooses the lesser of the two.