Skip to content

Think twice about using session state

April 7, 2012

So this question comes up all the time: “Where should I keep my shopping cart data for my application?” The common knee-jerk response is “use session state”. I find this to typically be the wrong answer if you want to build a resilient and scalable application.

Session state was originally meant for this kind of data (user provided data like a shopping cart). One thing session is not meant for is caching of user profile data. Back in classic ASP there were not a lot of other caching per-user data, so session was the obvious choice, but in ASP.NET (and just .NET in general) there are so many better options. One such example is the data cache in ASP.NET. With the data cache you specifically code to deal with failure (the cache item might not be present) and you can also set very specific conditions per cache item designating how long you’d like the cache the item. The data cache is very useful for a place to store data when you want to avoid round trips tot he database. But why not use session? Wouldn’t be essentially the same since it is also stored in-memory? Read on…

So back to session state as a place to store user driven data (i.e. shopping cart)… Session state is stored in-memory by default. This is a horrible option if you want to remember data for your users. In ASP.NET the hosting application pool frequently recycles and this is typically out of your control. This means that all that in-memory state is lost, which is why using session state in-proc is a bad choice. If you’re going to the effort to keep this data for the users, you simply can’t rely upon in-memory state — think shopping cart which equates to making sales on your website or simply the frustration of your users when all their data is lost and you have to make them start all of their shopping over again.

As an alternative you can store your session out-of-proc (in the ASP.NET State Service which is a windows service or in SQL Server or in a custom store). This certainly makes the state more resilient, but there is a cost to this resiliency. Each HTTP request into the application means that your application must make 2 extra network requests to the out-of-proc store — one to load session before the request is processed and another to store the session back after the request is done processing. This is a fairly heavy-weight request as well because the state being loaded is the entire state for the user for the entire application. In other words if you have ten pages in your application and each one puts a little bit of data into session, when you visit “page1″ then you’re loading all of the session data for “page1″, “page2″, “page3″, etc. By the way, these extra network requests happen on every page even if you’re not using session on the page. There are some optimizations (EnableSessionState on the Page and SessionStateAttribute for MVC), but they don’t solve the problem entirely because the network request must happen to update the store to let it know the user is still active so that it doesn’t cleanup inactive sessions.

So back to caching briefly: this is why we prefer to use the data cache for caching and not session. If you’re using session for any user generated data, then you can’t keep it in-proc and you should keep it out-of-proc and once it’s out-of-proc then that defeats the purpose of caching. The data cache is for read-only data that can be reloaded from the original data source when the cache expires and session is meant for user driven data like shopping carts. Except session has all these problems…

So what to do for our shopping cart data? Here’s what I suggest: 1) explicitly disable session in your application’s web.config (it’s enabled by default in the machine-wide web.config), 2) keep the user data elsewhere. Elsewhere might mean in a cookie, in a hidden field, in the query string, in web storage or in the database (relational or NoSQL). Which one’s the right answer? As always, it depends. For shopping cart like data, I’d probably use the database. I’d create a custom “shopping cart” table and as users add items then you make network call to update the database. Once the user places the order then you’d clear out rows for the user’s shopping cart. As for the key in the shopping cart table you can simply use the user’s unique authentication username from User.Identity.Name.

So the complaints about my conclusion are: 1) effort, 2) network calls, 3) abandoned shopping carts and 4) what about anonymous users.

1) Some people complain that this is too much work. I’m sorry to inform you that programming is hard. Get over it. And to be honest, it’s not that hard with the modern ORMs we have of if you’re using a NoSQL database then it’s even easier. Also, this database doesn’t have to be your main production database — it could just be a database that’s for the webserver.

2) What about all these network calls? Well, if you were using session then in-proc was a non-starter and you were going to have to keep this data out-of-proc. So you were already going to incur heavy-weight network calls. With this approach only the pages that need the shopping cart data incur the network calls and the pages that don’t need the shopping cart data won’t have this burden imposed upon them. This is already better than out-of-proc session.

3) With this approach you will need to have some way to periodically cleanup abandoned shopping carts. Sure, but the application is still better off for the effort. Also, this assumes that you do want to get rid of the data, but think about amazon — they don’t ever want to get rid of shopping cart data. That data is potential sales (they remind you on every visit that you forgot to give them your money) and it’s probably useful for data mining. So maybe keeping this data is a good thing.

4) I suggest that you should use the authenticated user’s username for the key in the shopping cart table. Well, what about anonymous users? Part of the Session feature is that there is a “Session ID” associated with the user and this is sometimes quite useful. It is, but not at the expense of at of the other parts of session. So what to do? Well, there’s a little known feature in ASP.NET called the Anonymous Identification Module. This is a HttpModule whose sole purpose is to track anonymous users with an ID. If you enable this feature, then it issues a cookie with a unique ID per anonymous users. You can then access that ID in your code via Request.AnonymousID.

One last reason session is problematic: concurrent requests. Access to session (by default) is exclusive. This means that if your page makes 2 Ajax calls back to the server, ASP.NET forces one to wait while the other executes because each request is taking locks on the session. This really hampers scalability if not responsiveness. Also, with some of the new HTML5 features like Server Sent events that hold long-lived connections to the server then this can lock a user out of the server if they try to make a request at the same time.

So beware anytime someone says “use session state — it’s easy”. If something’s easy then you’re making a tradeoff. Realize what you’re trading off and just take a minute to think about a better way to store the data. Your application will be better off.

If you’ve ever been to one of my ASP.NET or MVC classes then you’ve heard this before. For those that haven’t, I’ve been meaning to write this since 2006… better late than never.

29 Comments leave one →
  1. April 7, 2012 9:21 pm

    Finally, something I can link to when people ask. Very well written.

  2. dgerler permalink
    April 10, 2012 3:22 am

    Timely for my project and very informative!

  3. June 26, 2012 11:42 am

    Very interesting and well explained, Thanks !

  4. marianux permalink
    July 25, 2012 5:50 am

    Thanks, very usefull

  5. klinkerhofen permalink
    September 17, 2012 2:02 pm

    Loved it. Thank you

  6. November 2, 2012 12:05 am

    LEt’s say I make an application that have user and data( let’s call )BU. Some User have Permissions rights(read/write/grant) to some BU.
    The generating on those Permissions and BU are expensive , lenghty taks( think about delegating users, hierarchy of BU and hierarchy of Users)
    I need to access those BU and Permissions on BU FAST and SECURE .

    1. I can persist/retrieve (EASY) via Session – FAST and SECURE ( one time not so fast- first time retrieving)

    1. I can persist via QueryString – not SECURE -can be faked easily by user. More, browser can not send more than 255 ( or something) in query string

    1. I can persist via hidden inputs -again, not SECURE -can be faked easily by user.

    1. I can persists via cookies – again, not SECURE.

    It’s true that I can encrypt and decrypt data in 2,3,4 – but please read http://en.wikipedia.org/wiki/Security_through_obscurity

    So what are your options, if not SESSION ?

    • November 2, 2012 1:21 am

      @ignatandrei — For all the times we post to the same threads on the ASP.NET forums, I’m surprised you’ve not seem my other post about securing values (QueryString/hidden inputs/cookies) sent back to a user:

      http://brockallen.com/2012/06/21/use-the-machinekey-api-to-protect-values-in-asp-net/

      So it’s not fair (or accurate) to say that sending a query string/hidden input/cookie value back to a client is insecure. Forms authentication does it with its cookie value — you’d not say that’s insecure, would you?

      I’m not sure what the point is of your link about security though obscurity. I’m a firm advocate that security through obscurity is no security at all. I’m guessing you are as well given that you linked it.

      As for your expensive operation that you wish to cache in session — sure, that will work. Just realize that your requirement sounds like a caching requirement and there are better caching mechanisms out there; ASP.NET data cache, NCache, Azure cache, memcached, etc. Session, as I outlined above, is not meant for caching and is very coarse-grained for this type of thing. It can work (sort of), but I don’t feel as if it’s the best tool for the job (again, for the reasons I outline above).

      • November 28, 2012 4:28 am

        Hello Brock
        You have got a point with security.

        For my lengthy operations: why not cache directly in the session and use DataCache? Data Cache recycles with the application as too.
        So please give a real world example to follow.

        • November 28, 2012 11:35 pm

          The nature of the data used for data cache is different than session. You only put data in the ASP.NET data cache if the data can be reloaded from the database without the user knowing. Session state is intended for user-generated data (like a shopping cart) and if that data is lost (due to recycle) then you have to have the user re-enter the data (which is not desirable). So, data cache is for caching. Session is for user state management.

          And so for all the issues I listed above, in my opinion, session state is a bad option. And I list above all the alternatives, but it basically boils down to using a database for user-generated data if the data is important.

          • ignatandrei permalink
            November 29, 2012 1:51 am

            Being a lengthy operation, I do not want to expire( such the Cache expires) I want as long as possible. That’s why I put into the Session and not to the Cache.

  7. Bigb permalink
    December 3, 2012 1:00 am

    Completely agree with the article. To store user input there is more secure ways of doing this rather than “lazy-ugly way” like session.

    I think Encrypted hidden field is the best way to go if you are do not need this user data in the future, otherwise custom table like explained above.

  8. December 21, 2012 4:11 am

    It’s also worth noting that SessionState slows down your page even if you don’t actually use the session on the page (and even if you’re using the default in-proc session state), due to locking taking place. See http://stackoverflow.com/questions/3629709/i-just-discovered-why-all-asp-net-websites-are-slow-and-i-am-trying-to-work-out . The “fix” for this is to use read-only session state wherever possible (or turn it off) which doesn’t acquire the lock, or use a custom session implementation.

    • December 21, 2012 10:17 am

      No, the “fix” is to not use Session :)

      • December 21, 2012 10:19 am

        Yeah, that too – I only meant “fix” as in “fix for people that use the built-in session management” :P

  9. December 21, 2012 4:29 am

    Also there’s a few minor typos in your post. “of-of-proc” should be “out-of-proc” and “essentially the same sine it is also” should say “since” not “sine”. :)

  10. January 3, 2013 3:03 pm

    Reblogged this on Mike's Dev Blog and commented:
    Really useful paper on session state.

  11. April 10, 2013 8:47 pm

    What would be disadvantage of using cookies instead of database to store temporary data for unlogged user?

    • April 11, 2013 8:25 am

      Well, if you don’t want any one else to replay the cookie, you’d need SSL. Also, cookies are limited in size. Also, if you don’t want the end user to know the value you’d need to encrypt it (which adds to the size). Finally, if you don’t want the end use changing the value you’d want to sign the cookie (which adds to the size). Other than that, cookies would work to maintain a small amount of state.

  12. onefootswill permalink
    May 23, 2013 8:37 am

    This is such a good post! I’ve learnt a number of things here. I never knew about the Anonymous Identification module. Has it been deprecated? I only ask because one of your links has a “This Topic Is No Longer Available” heading.

    I’ve stumbled onto your stuff and MembershipReboot as I am trying to skill up in security (I’m a mid-level dev, not architect). And I’ve nary seen security done well on any project I’ve worked on.

    • May 23, 2013 8:49 am

      Nope, it’s not deprecated. I don’t remember what the broken link used to point to, so I removed it. You can see from the main docs how to enable it, so that should be sufficient.

  13. June 26, 2013 12:36 pm

    Thanks for this article it really helped me convert my existing shopping cart system to use caching.

    I was bit concerned about what I was going to use to identify anonymous users, but I eventually decided to use the shopping cart ID (Guid) as the cache key, which was also stored as a cookie, so I could access the correct cached item on a request by request basis.

    Also this has made it a lot easier to deal with on a cloud hosting environment.

    Thanks once again!

  14. ali permalink
    October 10, 2013 10:30 am

    very informative

  15. November 18, 2013 11:49 am

    Ok after reading this, you explained a lot of things I was questioning myself, since I am building the proper cart for the first time in .NET environment. I usually used session in PHP but now I want to know what is the best way.

    So if we talk about session vs database in terms of network performance why is the database faster in your way. So you are saying that performance wise expected from users side, database requests would not be much slower or even slower than session requests? Or you are just saying that session is bad because it loads on every page. What exactly do you mean with heavy network load?

    • November 18, 2013 4:00 pm

      I assume you mean my suggestion of a custom database? So my rationale is that a custom DB would only be used on the pages that need the shopping cart, and thus the pages that don’t use/need the shopping cart won’t make the DB round trip. So the perf is on the pages that don’t need the state. Also, the pages that do need the state will be, in essence, custom and thus can be better tuned. Session state’s DB code is one massive blob for all the accumulated state in the app (so shopping cart + anything else any other page has put in there). In other words, it’s too generic.

  16. January 14, 2014 8:52 pm

    Do you have any remark regarding scalability of database based sessions with expressed scalability issues. technet.microsoft.com/en-us/magazine/2009.06.asp.aspx

    • January 14, 2014 8:58 pm

      I think the distributed in-memory cache would be the only viable approach to using session state (other than not using session at all).

Trackbacks

  1. Adding Session support to ASP.NET Web API - StrathWebStrathWeb

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: