Introduction
There are many knobs available in WCF to tweak behavior as well as scalability and availability of your application. Furthermore, most of these settings are available via client and server side configuration files. While this makes the framework very configurable, deciphering how the various timeouts work can be challenging.
Here I look at two more commonly used timeouts and explain how they affect WCF client and service behavior when used via a binding that supports reliable sessions (ex: netTcpBinding). Both of the timeouts can be configured via client and server config file or programmatically. I will only show configuration file implementation as I believe in most cases using configuration files are a better option as it decouples application administration from development. If you are not familiar with reliable sessions, please refer to MSDN documentation to familiarize yourself as this article assumes basic WS-reliable session understanding.
Background
Reliable sessions provide message level guarantees for WCF communications between client and server -or more accurately, guarantees to report failure when message delivery fails. This is achieved by use of transport level acknowledge (ACK) sequences for each message shared between client and server. Each reliable session established has its lifetime governed by a combination of inactivityTimeout
and receiveTimeout
properties in client and server bindings. When one session expires, a new session is established on subsequent message initiation between client and server. Understanding how to control the lifetime of the sessions via the timeouts is important because establishing new sessions has infrastructure overheard which can affect scalability and availability of your application.
Before we go further, let's define two key terms I will use when talking about timeouts:
- Infrastructure level message (ILM): These are channel level messages exchanged between client and server communication stack. They are purely infrastructural in nature; examples of such messages include ACKs and K-As (keep alive.) They have no application level overhead associated with them such as SOAP headers etc.
- Application level message (ALM): These are WCF SOAP messages exchanged between client and server as defined by the data and operation contracts. When an ALM is shared between server and client in a reliable session, ILMs are also generated to acknowledge receipts of the messages.
Without going into detail, suffice it to say that typical wcf client implementations should always cache channel factories to generate client side service proxies. WCF internally caches channel factories when proxies are generated using svcutil
or when they are created using ClientBase<T>
implementation. Doing so is a best practice and has numerous advantages including support for connection pooling (not discussed in this article.)
Reliable Session: Inactivity and Receive Timeouts
A reliable session once established between a client and server stays alive while neither receiveTimeout
nor inactivityTimeout
have expired. If either expires, the reliable session is dropped and a communication fault is generated by WCF infrastructure that informs the client of the same. Client can handle this fault appropriately.
Table 1 is a matrix of inactivityTimeout
and receiveTimeout
applicability to WCF client and server behavior.
Let's look at an example to further elaborate these settings. Below are sample server and client side configurations.
A few quick notes about the configurations:
- I use
customBinding
in the above example, but the reliable session throttles work identically for any other binding that supports reliable sessions. - I do not show all the configuration elements available with reliable sessions or custom bindings, but focus on the two timeouts that are explained in this article.
- A comprehensive explanation of reliable session elements and bindings that support reliable sessions is available on MSDN help pages.
Service binding configuration
<customBinding>
<binding name="reliableCustomBinding"
openTimeout="00:00:30"
closeTimeout="00:00:30"
sendTimeout="00:05:00"
receiveTimeout="00:20:00">
<reliableSession acknowledgementInterval="00:00:0.2"
inactivityTimeout="00:10:00"/>
<binaryMessageEncoding>
<readerQuotas
maxArrayLength="500000"
maxBytesPerRead="65536"
maxDepth="16384"
maxNameTableCharCount="500000"
maxStringContentLength="500000" />
</binaryMessageEncoding>
<tcpTransport portSharingEnabled="false"
maxBufferPoolSize="32768000"
maxBufferSize="524288"
maxReceivedMessageSize="524288">
</tcpTransport>
</binding>
</customBinding>
Let's explore the server configuration example further:
- Receive timeout is set to 20 minutes. This indicates that if no application messages are exchanged between a client and service after the RS-channel is established for 20 minutes, then that channel will be torn down by the server and a communication fault will be raised. If
receiveTimeout
is not specified in reliable session configuration element, it defaults to 10 minutes (always check MSDN for default values for the specific .NET framework you are targeting as MS can change them for future .NET versions.) - Inactivity timeout is set to 10 minutes. This indicates that if no infrastructure level messages (ACKs or K-A) are received for 10 minutes on the established RS-channel, it will be torn down and a communication fault will be raised. If
inactivityTimeout
is not specified, it defaults to 10 minutes (same as we have configured in this example.)
Note, that if either timeout on the server expires, then the reliable session channel will be faulted and client notified via a communication fault.
Client binding configuration
<customBinding>
<binding name="reliableCustomBinding"
openTimeout="00:00:10"
closeTimeout="00:00:10"
sendTimeout="00:05:00">
<reliableSession acknowledgementInterval="00:00:0.2"
inactivityTimeout="00:01:00"/>
<binaryMessageEncoding>
<readerQuotas
maxArrayLength="500000"
maxBytesPerRead="65536"
maxDepth="16384"
maxNameTableCharCount="500000"
maxStringContentLength="500000" />
<tcpTransport maxBufferPoolSize="1966080"
maxBufferSize="524288"
maxReceivedMessageSize="524288">
</tcpTransport>
</binding>
</customBinding>
Now, let's look at the client configuration:
- Receive timeout is not applicable for a client.
- Inactivity timeout is set to 1 minute. On the client side, inactivity timeout is used to generate ILMs periodically to keep a channel from faulting due to server inactivityTimeout setting. WCF uses half the inactivityTimeout setting to generate an ILM. In this case, every 30 seconds an ILM will be generated from the client on the established RS-channel which will reset the server-side inactivityTimeout timer (set to 10 minutes in our case)
WCF Tracing
You can capture transport level messages via WCF tracing to see the ILM messages generated between client and server due to the client inactivityTimeout
setting. These messages will look similar to the sample logs shown below which have been generated using the client and service configurations in this article.
Notice that ILMs are generated every 30 seconds and will continue to be generated till server receiveTimeout
expires due to no ALMs(20 minute) since server inactivityTimeout
is satisfied by the client inactivityTimeout
configuration.
Analysis
At this point, you may be wondering the purpose of two timeout values that faults the same channel; and what is the point of setting one timeout to be greater or smaller than the other? Based on my experiences, here are the reasons:
- In my experience client-side
inactivityTimeout
is useful when the client is interested in knowing within a certain amount of time that the service is unavailable. This can be achieved by using inactivity timeout on the client and handling communication faults. - Service-side
receiveTimeout
is relevant in configuring services for scalability/availability such that application resources can be recycled after an optimal amount of time has passed in which client-server have not exchanged an application level message. - Service-side
inactivityTimeout
can be used to tear down and recycle connections with clients that are inactive for a period of time as deemed optimal by networking constrains. This can provide a way to free resources sooner than the application level receiveTimeout
threshold. - These two timeouts have slightly different audiences -
receiveTimeout
is something an application developer should understand care about; inactivityTimeout
is a deployment consideration and should be something with which application administrators should be familiar.
Conclusion
In summary, when using RS-channels:
- Set server
receiveTimeout
to how long you are willing to accept clients to be connected without exchanging ALMs. Balance this with application and service resources that may be tied up to your client especially when your service is instantiated per-call. - In conjunction, server
inactivityTimeout
should be set to how long you are willing to accept clients to be connected without exchanging ILMs. This should be balanced with the demand of network resources and the infrastructure available for your service. - Set client
inactivityTimeout
to generate ILMs to keep an idle RS-channel alive till receive timeout expires due to no ALMs. - Do subscribe to communication faults to learn when your RS-channel has been faulted.
I hope this gives you a good overview of these two timeouts and some ideas on how to use them well.
History
- March 23, 2014: Initial Article