Scott Hanselman

Troubleshooting Expired ASP.NET Session State and Your Options

January 30, '08 Comments [23] Posted in ASP.NET | Bugs
Sponsored By

I have a love/hate relationship with the ASP.NET Session. It's such a convenient place to put things, but when you start putting applications into production there are a number of less-than-obvious edge cases that can come up and bite you.

Most often the Session is used when managing state over a long process like a multi-step wizard or questionnaire. However, when people use the Session, they often lean on it a little. They'll bake it into their design so deep that when it doesn't work, they're screwed. That's not to say they shouldn't be able to lean on it, I'm just saying that there's a lot of things going on with Session (not just on ASP.NET, but other frameworks as well) in order to get it to look seamless.

Built in Options

ASP.NET offers three options (four if you count rolling your own).

  • Inproc - The default, and usually works fine. However, you can get into trouble in a few scenarios.
    • Web Farms - If you have more than one web server, it's important to remember that your users may not "stick" to the same webserver with each request. Some routers offer Sticky-Sessions or the ability to "pin" a user to a server. This works well if the router uses cookies as its key, but it's less reliable if the router uses IP address/source port as the key as these may change, especially if the user is behind a mega-proxy.
    • Web Gardening - If you've setup IIS to run multiple instances of the IIS Worker Process on a single multi-proc machine, this is the equivalent of running a Web Farm, just on one machine. This technique is usually only useful when you've got a very CPU-intensive application - in other words, don't just turn on Web Gardening and expect your problems to get better instantly. It's subtle.
    • Unexpected Process Recycling - IIS6 had some wonky defaults and would recycle the AppPool or Process when some certain limits were hit, like after x number of requests or after 20 minutes. This is the classic "flaky session state is expiring" issue that lots of folks hit. You'll be more likely to see this if you've got really long running processes where users are logged in for long periods of time.
  • Out of proc - A good next step, this moves session out to a Windows Service. You can run one per Web Farm (meaning, you've got multiple machines but one instance of this service) and your session data will survive process recycles, but not system reboots. This is useful for both Web-Gardening and Web-Farming.
    • Folks usually forget to mark their objects as [Serializable] which basically gives your objects "permission" to leave their process space and be stored in memory in the State Service. If you've got a high-traffic site you might want to avoid storing complex objects and object graphs as you'll pay for it on the serialization. Of course, with all things, measure everything! You'll get best performance if you stick with basic types like strings, ints, etc.
    • UPDATE: I wanted to update this post and point folks to Maarten Balliauw's most excellent series on Out of Proc Session State (StateServer). He covers the basic setup, which is unremarkable, but then digs into the advanced stuff including "partitionResolvers" which I am ashamed to say I hadn't heard of! Recommend.
  • SQL Server - The most robust, but now you'll pay for not only serialization, but storage. However, SQL Server is a highly tuned system and if you've got a site with any significant traffic I really recommend just skipping out-of-proc and putting your session state into a SQL Server with a lot of memory. Rather than trusting ASP.NET out of proc Session State Server to be a small database, leave the database work to the databases.
    • The benefits of SQL Server for your Session State include surviving process recycles and reboots.  but more importantly using removes a lot of variables from your troubleshooting in the sense that you no longer worry about the storage of your Session, now you just need to worry if your Session Cookies are getting passed back and forth from browser to server.
    • Make sure you're using Windows Integrated Security and that you decide if you want ASP.NET to store Session in tempdb (which won't survive a SQL recycle) or a dedicated database (my recommendation).

Troubleshooting

There's a number of things that can go wrong, some of which I mention above, but here's what I usually run through when troubleshooting things.

  • Is the ASP.NET SessionID Cookie actually moving back and forth between browser and server. This can be confirmed by:
    • Using an HTTP Sniffer like ieHttpHeaders or HttpWatch or Fiddler and confirming that the Session ID cookie's value isn't changing between requests.
    • Confirming that the cookie isn't being blocked by IE, privacy settings, lack of a P3P policy document, local firewall like ZoneAlarm or Symantec, or a corporate proxy with an attitude problem.
  • Is IIS recycling  the AppPool or Worker Process? Confirm the settings in IIS manager and make sure they are right for what you're doing.
  • Is the session timing out? Are you sure you're hitting the same VDir from whence you came and successfully resetting the sliding expiration on the Session ID?
  • Is some other thing like an Ajax call or IE's Content Advisor simultaneously hitting the default page or login page and causing a race condition that calls Session.Abandon? (It's happened before!)

At my last company Session became such a hassle for large high traffic applications that we just stopped using in-proc and started exploring alternatives.

Some 3rd Party Session State Options

  • NCache from Alachisoft - An in-memory object cache that's distributed across your web farm. Think of it like Out of Process Session State, but distributed/clustered in their Enterprise Edition.
  • ScaleOut Software SessionServer - Fast, scalable in-memory storage that is distributed across machines. Full Disclosure: we worked with these guys while I was at Corillian, but never put them into production.
  • Memcached Session State Provider - Fahad has created ASP.NET Session State providers that will talk to memcached, a very popular distributed memory caching system originally created for LiveJournal.com and now used all over.

Related Links you might enjoy

How do you manage state at your company?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, January 31, 2008 12:11:26 AM UTC
Scott,

One of the five best technology decisions that Gratis Internet has ever made has been deploying ScaleOut State Server. Our web sites are popular and when we used SQL as our session provider we were seeing 15,000 transactions per second just related to session management. As any DBA will tell you, it takes some thought when you're running that volume.

SOSS was able to handle it with ease. We process tens of thousands of session-related reads/writes every second and SOSS doesn't even blink. It's fully distributed, so if a web server goes down it is seamless to the end user. It also "just works". It's actually one of the more remarkable products I've ever had the pleasure of working with. Microsoft should buy them and include the product with Windows in my opinion.

No, I am not affiliated with the company in any way, just a huge fan.
Thursday, January 31, 2008 12:12:30 AM UTC
Also, FWIW, the product is called "StateServer" not "SessionServer".
Thursday, January 31, 2008 12:22:05 AM UTC
Actually they have 3 products now...
Thursday, January 31, 2008 12:53:21 AM UTC
We had problems when we changed our complete web into a 1 page AJAX driven site. Our solution was to have a javascript timer ping the server at regular intervals. This kept the session alive.
Bydia
Thursday, January 31, 2008 1:17:52 AM UTC
I've used my "Compressed Cookies" LZMA solution in lieu of Session in many instances. You can get over 9000 lines of "stuff" compressed with the BinaryFormatter into a cookie. Just create your own serializable custom class to hold whatever you need, and pass it into the compressed cookie "thing". It's reliable and fast. And it doesn't suffer from ASP.NET process recycling.
Thursday, January 31, 2008 1:34:26 AM UTC
Is anyone actually using memcache for Windows in a production environment? I was looking at using it as a distributed cache for NHibernate, but I was trying to find some feedback on anyone who was actually using it.
Eric Hauser
Thursday, January 31, 2008 1:47:45 AM UTC
Peter - Good idea! But, do you really want 9k cookies? What's a typical size for you?

Eric - I don't know, but I sure hear it mentioned a lot.
Thursday, January 31, 2008 1:49:21 AM UTC
We are currently using the ASP.Net built-in version of SQL Server session management and have discovered after upgrading to .Net 3.5 that it is not possible to store LINQ-To-SQL generated objects in session because they do not implement the [Serializable] attribute. It is not even possible to add the [Serializable] attribute due to the internal change tracking mechanism not being serializable.

I have read various reasons why this is so, but I can't help but be frustrated by this especially when it is so simple to have the objects support [DataContract] (just select Serialization = Unidirectional on the DBML file's properties). To me this seems like a major oversight on the part of the LINQ-To-Sql team...

[b]Take-Away: If you are not using In-Proc session storage, think long and hard before trying to use LINQ-To-SQL.[/b]

Troy Goode
Thursday, January 31, 2008 1:50:29 AM UTC
Troy - Good info! What are the reasons?
Thursday, January 31, 2008 1:52:57 AM UTC
Troy - I'm digging into this internally to see if there's a good way to make this happen.
Thursday, January 31, 2008 2:01:35 AM UTC
The short-answer is that it becomes problematic to serialize LINQ-To-SQL objects because of the lazy-loading of data that occurs. Say you have a "Category" object that has a property named "Products" that is of type List<Product>. Should the list of Products be serialized with the Category object? If not, what happens when you serialize the Category object, then deserialize it and attempt to access the Products property? If you serialize everything you wind up with massive objects, especially while working with hierarchical data (imagine a CMS content tree). I will try to find the longer explanation for you, but that seems to be the gist of the reasons I was able to find.

The reason I find this frustrating is that I feel these same issues apply to serialization via WCF using [DataContract], yet that scenario was worked through...
Thursday, January 31, 2008 2:05:05 AM UTC
Thanks Scott,

Currently I am using some SessionHelper code I brewed up internally which interrogates an object and if it supports [DataContract] but not [Serializable] it will use the DataContractSerializer convert the object to a string and manually store the serialized form. I do the reverse for retrieving objects. So far this is working, but I have yet to do the work necessary to store/retrieve a List<X> where X is not serializable...

All in all it is possible to work around it, but seems very hackish.
Thursday, January 31, 2008 5:14:36 AM UTC
We use session state a lot (we have no choice, it's the nature of our site), our solution is multiple out of process session state servers, and implementing the IPartitionResolver to decide which session state server to use. Basically hash the session id, mod with the number of servers you have to decide which server to use. The downside is a session has affinity with a session server, so no fail over, but good enough load balancing. We have found the session state service to be very stable, so haven't had any problems. This was achieved with about 10 lines of code and a extra web.config setting.



Aaron
Thursday, January 31, 2008 9:44:40 AM UTC
Hi Scott,

We use Sooda O/R Mapper and serialized transactions stored on server hdd. It's simple solution but very scalable. On client we have only unique indentifier of that transaction :)
Thursday, January 31, 2008 2:58:08 PM UTC
Unfortunately, some of the build in objects in the .NET framework are not marked with the [Serializable] attribute -- I've been bitten by this and it's quite an annoyance to have to re-implement something that is already available.
I can't remember which ones it was right now, but there didn't seem to be any specific reason for it.
Tobias
Thursday, January 31, 2008 4:08:28 PM UTC
Just my tips on Session. Just pretend InProc doesn't exist. You will eventually grow out of it. Its not as seamless as one might think switching from INProc to something else - you may run in to Serialization issues, so just don't do it.
Dave
Thursday, January 31, 2008 4:55:41 PM UTC
@Tobias
You may be thinking of Dictionary<TKey, TValue>. It's not serializable.

@Scott
While you're checking on Troy's request, could you also get an authoritative answer from the BCL team on why generic dictionaries weren't made serializable? I've heard so many different reasons; it would be nice to get the straight scoop.
Thursday, January 31, 2008 5:43:37 PM UTC
I think very carefully before I employ server side state at all. If I can keep state at the client then I'm very happy. The LZMA compression technique for storing lots of stuff in a cookie sounds interesting. I often use an Authentication token in a cookie and the ASP.Net Cache combined with a database to validate it; if the client hits another server, that does not have a copy of the cached token, it's checked from the database.

A combo of sensible caching and sticky servers helps me avoid Session completely.
Thursday, January 31, 2008 9:34:15 PM UTC
PWillis - They look serializable...are you thinking of another type?


[Serializable]
[System.Runtime.InteropServices.ComVisible(false)]
public class Dictionary<TKey,TValue>: IDictionary<TKey,TValue>, IDictionary, ISerializable, IDeserializationCallback {
Thursday, January 31, 2008 10:03:12 PM UTC
@Scott,
Forgive my loose language earlier. Generic dictionaries aren't XML serializable. I was guessing at Tobias's intent, since the non-XML-serializability (is that a word?) of IDictionary is one of the more commonly-encountered "gotchas" that I saw in my consulting days.

Thankfully, the blogosphere is rife with workarounds. (Here are three.)

Considering that all three of the above articles begin with the statement "for an unknown reason, IDictionary isn't XML serializable", I'm still curious: why?

PS: As Scott pointed out to me, the SQL Server Session Provider uses binary serialization, my original comment wasn't actually relevant to this topic. My bad.
Friday, February 01, 2008 12:27:11 AM UTC
Scott:

Thanks for mentioning ScaleOut StateServer! I wanted to point out a couple of ways in which a distributed cache like SOSS can further boost performance for managing session-state. First, SOSS has a transparent, internal, client-side cache so that it avoids repeatedly fetching session-state from the out-of-process distributed cache and deserializing it on every Web hit. To give you an example, we saw a 5X reduction is response time for a 100KB dataset when retrieving it out of the client-side cache. SOSS automatically keeps the client-side cache coherent with the distributed cache.

Second, you can really optimize performance by bypassing the ASP.NET session-state object and storing data directly in the distributed cache using the APIs; consider keeping only some bootstrap information, such as cache keys, in the session-state object. This gives you full control over when large objects are read, and more importantly, when they are updated. For example, a shopping cart would be serialized and updated to the distributed cache only when it actually changes and not on every Web hit, as would be the case with session-state. Maintaining finer-grained control over updates lets you fully optimize overall access performance, which can make a big difference for both network and CPU overhead.
Friday, February 01, 2008 4:59:49 PM UTC
@PWills
Hmmm - I don't think that was it. But that just one more, then.
Tobias
Thursday, February 07, 2008 7:25:49 PM UTC
Hi Scott,

You mentioned you worked with ScaleOut's SessionServer but hadn't implemented in production. Have you also worked with StateServer? Any opinions on the product?

Thanks!
Ian Suttle
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.