WCF inactivity timeout=infinite
One of my last projects in Electrabel has involved the creation of a 3-tier application using the .Net framework. During the development of the application we decided to use a number of libs that we though would ease the development of the application, this frameworks were:
- Spring.Net
- NHibernate
- NVelocity
- WCF
As many of you may know already, adding high level frameworks to a project is a double-edge sword. While adding this kind of frameworks does indeed accelerate the initial development of the application, they do complicate the process of hunting down bugs since you really need to know how the frameworks have been designed.
During our development and due to a small mistake we found or selfs in one of those situations in which we really had to think about the interaction between WCF and Spring.Net.
Spring.Net and WCF integration documentation
Before the project I did not have much experience with Spring.Net and therefore trusted the designer other developer that was working with me. As any developer would do, he read the documentation from Spring.Net in order to integrate WCF and Spring.Net through Springs IoC container. If you read Springs documentation you will find the following:
28.4. Creating client side proxies declaratively
To create a client side proxy based on the use of ChannelFactory
, you can use this rather ugly ‘boiler plate’ XML snippit that takes uses Spring’s support for calling factory methods on object instances. <!-- returns ChannelFactory<ICalculator>("calculatorEndpoint").CreateChannel() --> <object id="serverAppCalculator" type="Spring.WcfQuickStart.ICalculator, Spring.WcfQuickStart.ClientApp" factory-object="serverAppCalculatorChannelFactory" factory-method="CreateChannel" /> <object id="serverAppCalculatorChannelFactory" type="System.ServiceModel.ChannelFactory<Spring.WcfQuickStart.ICalculator>, System.ServiceModel"> <constructor-arg name="endpointConfigurationName" value="serverAppCalculatorEndpoint" /> </object>
Well, that seems easy! After a number of evenings wasted on this, I can tell you it is not!
Clean up correctly when exceptions occur with a WCF client
Before I start complaining about the Spring.Net documentation it is interesting to read a very nice post at MSDN that addresses side effects of the using block when working with WCF. Take some time and read it.
From that article the part I am the most interested is the one related of how to clean up correctly when exceptions occur, for example:
try { ... client.Close(); } catch (CommunicationException e) { ... client.Abort(); } catch (TimeoutException e) { ... client.Abort(); } catch (Exception e) { ... client.Abort(); throw; }
Spring.Net and WCF problems
I am sure that you are smarter than me and you can easily spot the error… well I could not, and for those of you who want to know here it is:
Spring.Net will create the WCF clients as Singletons and will inject them in your objects. Of course this does not mean that Spring.Net will be the nice guy that will ensure that your clients are cleaned correctly when there is an error! Guess what, we use to have errors, to be precise, we use to have issues related with the inactivityTimeout used in our clients.
We (I, since at that point I was the only developer in the project) did not know that the issue was related with the integration of Spring.Net and WCF and therefore googled for the inativityTimeout exception I was getting in my logs.
InactivityTimeout == infinite
If you google how to get the inactivity timeout to be infinite you will find the following solution:
The default receive timeout is 10 minutes, so WCF client will be disconnected after the idle time exceeded that limitation. What can I do if the connection needs to be kept alive?
Solution #1 – Server provides a dummy operation for client calls it regularly to let it not idle.
Solution #2 – Enable reliableSession and set receiveTimeout and inactivityTimeout to “infinite” in both the client and server.
The configuration snippet may like the following:
<system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding" receiveTimeout="infinite"> <reliableSession inactivityTimeout="infinite" enabled="true" /> </binding> </wsHttpBinding> </bindings> <services> ... </services> ... </system.serviceModel>You can get more detail explanation in the following reference link.
Reference in MSDN:
Binding.ReceiveTimeout Property
Guess what, I did that, and after implementing the solution, my clients will crash every 20 mins, WTF!!!
Hosting WCF services in IIS with reliable sessions
At this point I’m going to let you the last piece of the puzzle, the services were hosted on IIS and I was the only person testing the application… Right now, the smartest of you probably know the issue, the other like me will say, and what??.
Well, the issue is the following, IIS does also have a timeout that will recycle that process in IIS when it is not used. Since we are using sessions, when the process is recycled the sessions is lost and the clients crash, again….. This made us realize that we should correctly implement the clients by hand and just inject the ChannelFactories through Spring.Net and completely ignore the Spring.Net documentation.
Actual implementation
As you might have noticed already cleaning correctly the client is quite a lot of typing for what you have to do, to simplify things I implemented to methods that will take care of it:
public void CleanClientCall(Action action, ICommunicationObject client) { try{ action(); client.Close(); } catch (CommunicationException e){ ... client.Abort(); } catch (TimeoutException e){ ... client.Abort(); } catch (Exception e){ ... client.Abort(); throw; } } public TResult CleanClientCall<TResult>(Func<TResult> action, ICommunicationObject client){ try{ var result = action(); client.Close(); return result; } catch (CommunicationException e){ ... client.Abort(); } catch (TimeoutException e){ ... client.Abort(); } catch (Exception e){ ... client.Abort(); throw; } }
Well, in summary:
- Do not follow the documentation of Spring.Net and inject the factories, not the clients.
- Clean the clients correctly.
- Do not set timeouts to infinite, do things correctly, otherwise you will have problems later.
- Be lazy and use helpers methods to wrap the clean up if you can
I know this took me some time to fix and I hope it helps the next pour soul that has this problem.
