Think twice about using session state
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.
Finally, something I can link to when people ask. Very well written.
Timely for my project and very informative!
Very interesting and well explained, Thanks !
Thanks, very usefull
Loved it. Thank you
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 ?
@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:
https://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).
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.
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.
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.
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.
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.
No, the “fix” is to not use Session :)
Yeah, that too – I only meant “fix” as in “fix for people that use the built-in session management” :P
Yea, I know. I was just being a nudge. :)
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”. :)
Thx. I’ll fix it up.
Reblogged this on Mike's Dev Blog and commented:
Really useful paper on session state.
What would be disadvantage of using cookies instead of database to store temporary data for unlogged user?
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.
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.
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.
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!
very informative
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?
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.
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
I think the distributed in-memory cache would be the only viable approach to using session state (other than not using session at all).
So far i have used session state in every cases but from today i will think twice. Thanks for nice explanation…
Thank you for this really informative article. I’m still going to use Session sometimes because it’s easier and I believe as a developer we always have to decide, based on the needs of the current project, how much effort you should put into making something 100% reliable. But now I know what I’m getting myself into if I do use Session for that information, and also I’ll know better than to get myself into it on projects that need better reliability.
Hi Brock, interesting post again. Do you see the same problems with the new SQL Server 2014 in-memory option? http://blogs.msdn.com/b/kenkilty/archive/2014/07/03/asp-net-session-state-using-sql-sever-in-memory.aspx
From your link read the section: “Making In-Memory Session Highly Available”
It sounds to me like you can make the storage persistent across database restarts but it is turned off for performance reasons.
I also came across this solution which seems to address the author’s problems with session without requiring changes to existing code.
http://blogs.msdn.com/b/webdev/archive/2014/05/12/announcing-asp-net-session-state-provider-for-redis-preview-release.aspx
Yes, a redis provider is an interesting tradeoff to the problem. But designing large stateful application on HTTP might eventually be a recipe for disaster.
I came across this post recently and I was intrigued. We have an ASP.NET app hosted on Azure and we are currently using session state held in the distributed in-role cache. I imagine if we were to switch to using the cache directly we would need to find something to uniquely key the cache entries to each ‘session’?
However it does seem to me that using session state in this configuration is viable. We have quite small sessions. The performance hit should be minimal and there shouldn’t be any worries about recycling unless I’m mistaken.
What are your thoughts on this method of session state?
I don’t know what an “in-role” cache is. I’m guessing some out-of-proc distributed cache? But yea, the session ID is easily the key into that cache. Anyway, the point of the post is to get people to think about this stuff, and not just assume session is magic.
Reblogged this on juanhoang.
Hi,
Readers should consider that in modern programming caching refers mostrly to reading common, shared or periodically updated data from temp storage primarily for performance reasons. Storing any business information (like shopping cart mentioned in this post) should reside always in a more stable and endurant service like a database. The existence of a read/write cache in any system doesn’t dictate that it should be used because it’s there. Not everything is caching, therefore noone should waste time to decide how to convert an in-memory cache (ex Session), into a …generic database storage.
System.Caching.MemoryCache provides the infrastructure to save round-trips for non live data, but updates should be always at the DB. (Get it, Cache it, Get it again if anything in the source changes).
Regards.
I think that was part of my point — session needs something durable. For data that’s cacheable, then don’t use session.
I have a high traffic web application where I’m managing user shopping cart and online quiz data in a session using a Redis provider by installing it as a windows service. And I set the session timeout to be 5 days, so at the time I’m writing this I almost have 20k keys stored in Redis database.
But I realized even though I’m using Redis, once in a while customer’s starts getting very strange behavior during there online quiz and I end up getting lots of customer call.
I’m curious to know your opinion about Redis, is this a good choice for a high traffic app?
I’m not sure — I’ve not used it for a large session store as you have. I’ve heard that it doesn’t really provide durability — IOW there’s a possibility that you might lose data if a node goes down.