Caching user data in asp.net application - asp.net

What's the best way to cache web site user data in asp.net 4.0?
I have a table of user settings that track all kinds of user or session specific stuff like the state of UI elements (open/closed), preferences, whether some dialog has been dismissed, and so on. Since these don't change very often (for each user, anyway) but are looked up frequently it seems sensible to cache them. What's the best way? These are the options I've identified...
Store them in HttpContext.Current.Session directly (e.g. Session["setting_name"])
Store them in HttpContext.Current.Cache
Use a global static dictionary, e.g. static ConcurrentDictionary<string,string> where the key is a unique userID + setting name value
Store a dictionary object for each session in Session or Cache
What's the most sensible way to do this? How does Session differ from Cache from a practical standpoint? Would it ever make sense to store a dictionary as a single session/cache object versus just adding lots of values directly? I would think lookups might be faster, but updates would be slower since I'd have to re-store the entire dictionary when it changed.
What problems or benefits might there be to using a global static cache? Seems like this would be the fastest, but I'd have to manage the size. I could just flush it periodically if it hits a certain size, or keep a cross reference queue and remove things oldest first when it gets to a certain size. Does this make any sense or is it just trying too hard?

Session may end up being stored out-of-process or in a database, which can make retrieving it expensive. You would likely be using a session database if your application is to be hosted in a server farm, as opposed to a single server. A server farm provides improved scalability and reliability, and it's often a common deployment scenario. Have you thought about that?
Also, when you use Session not in-process, it ends up getting serialized to be sent out-of-process or to a database, and deserialized when retrieved, and you are effectively doing what you describe above:
... updates would be slower since I'd have
to re-store the entire dictionary when
it changed. ...
.. since, even if you use individual session keys, the entire Session object for a user is serialized and deserialized together (all at once).
Whereas, Cache would be in memory on a particular server in the farm, and therefore much more efficient than going out of process or to the database. However, something in cache on one server might not be in cache on another. So if a user's subsequent request is directed to another server in the farm, the cache on that server might not yet hold any of the user's items.
Nevertheless, I'd suggest you use Cache if you're caching for performance reasons.
p.s. Yes, you're trying too hard. Don't reinvent the wheel unless you really need to. :-)

might be better to put your information into memcached for scalability

Related

A non-locking in-process ASP.NET session state store

I'm using ASP.NET's in-process session state store. It locks access to a session exclusively, and which means concurrent requests to the same session are served sequentially.
I want to remove this implicit exclusive lock, so multiple requests per session can be handled concurrently. Of course, I'll be synchronizing access to the session state myself where it's applicable.
I'm using the MSDN documentation of Session State Providers to write my own session state provider, and this SO question pointed me to this example code of implementing this as an HTTP module, but the code looks suspiciously complex just for the purpose of removing the lock.
I should probably eventually implement the session state using ASP.NET's cache, and stop using the built-in session, like Vivek describes in this post, but for now how would I just like to remove the locking.
Any ideas or sample implementations?
Not the answer you're looking for, but I think that even if it were possible, changing the way SessionState works in this way is a terrible idea.
Think of the poor guys who will have to maintain your code down the line. The fact that Session serializes requests in this way means ASP.NET developers often don't need to worry too much about thread-safety.
Also if someone adds a third-party component that happens to use Session, it will expect the usual guarantees regarding locks - and you'll suddenly start getting Heisenbugs.
Instead, measure performance and identify specific areas where you need requests to process concurrently - I bet there'll be few of them - and carefully implement your own locking mechanism only for the specific items involved - possibly the solution that you're planning eventually using the ASP.NET cache.
If you are only reading from the session on a given page you could use the Page directive:
<%# Page EnableSessionState="ReadOnly" %>
to indicate the readonly nature and remove the exclusive write lock which will allow concurrent requests to this page from the same session.
As internally a ReaderWriter lock is used a reader lock will block a writer lock but a reader lock won't block reader lock. Writer lock will block all reader and writer lock.
If you need to both read and write from the same page to the session I think that what you've already found as information is more than sufficient. Just a remark about replacing the Session with Cache: while session is reliable, cache is not meaning that if you put something into the cache you are not guaranteed to retrieve it back. The ASP.NET could decide to evict the cache under some circumstances like low memory pressure so you always need to check if an item exists in the cache before accessing it.
but the code looks suspiciously complex just for the purpose of removing the lock.
That's because you are thinking of it as a simple lock someone added by chance / or laziness, instead of a factor that was considered in the whole session provider concept. That code looks simple enough for the task at hand / replacing the whole existing provider with your own / and while doing so lock in a different way than it is intended.
I suggest you read a bit more about how the session concept works in asp.net. Consider that the way it was designed involves reading the Whole Session once in the request, and then writing the changed session once.
Also note that while its not your scenario, code might depend on reading separate session values to process something and can write separate values as well / locking individually can get you into the same considerations that can cause a dead lock in databases.
The designed way contrasts with your intent of reading/writing+locking as each separate item is loaded/stored.
Also note that you might be updating the values of an object you retrieved from the session and not setting it back to it, yet the session was updated. As far as I know, the providers write everything in the session back at a certain point in the life cycle, so its not straightforward if you want to separate read vs. write locks at an item level.
Finally, if you are finding a high level of resistance from the framework session providers model and the way you intent to modify it will prevent you from using some features of it (as switching to a diff provider, since those would have the same issue); then I think you should just stay away from it and roll your own for your very specific needs. That's without even using asp.net Cache, since you want to it to be your way, you control the lifetime & locks of what's stored in there.
It just appears to me that you need a different mechanism altogether, so I don't see the benefit of trying to bend the framework to do so. You might reuse some very specific pieces, like generating a session id / and cookie vs. cookie-less, but other than that its a different beast.
You will have to write your own Session State Provider, it is not that hard as it seems. I wouldn't recommend you to use Cache because as Darin said Cache is not reliable enough, for example items there expire and/or removed from cache when there is not enough memory.
In your Session Store Provider you can lock items of session instead of the whole Session state (which suits us in our project) when writing, the essential code for this should be in ISessionStateItemCollection's implementation (in indexer), something like this:
public object this[string name]
{
get
{
//no lock, just returning item (maybe immutable, it depends on how you use it)
}
set
{
//lock here
}
}
For our project we have created implementation which stores items in AppFabric Cache and rely on GetAndLock, PutAndUnlock methods of the cache. If you have in-proc Session then you might just need to lock(smthg) {}
Hope this helps.
I suggest that you look into moving your sessions into SQL Server Mode. SQL Server sessions is to allow for any number of requests from multiple web servers to share a single session. As far as I know, the SQL Server manages the locking mechanism internally which is tremendously efficient. You also have the added bonus of having a lower in-memory footprint. And you don't need to write an HttpModule to run it.

What is the best way to manage one's session variables?

In my .NET web app, I keep basic user info in a user session object. I also usually keep a director class in the session; which is basically just has info about whatever thing it being worked on on that screen (like a customer id).
I am trying to keep from adding a ton of sessions. I also want to make sure at any given time ONLY the sessions that are necessary are in memory.
This means I need an effective way of managing my session variables. Any suggestions?
Two things:
1 - You should not be storing so much in session that managing memory is a concern. I.e., don't store objects in session, store pointers to things, e.g., rather than an instance of User just store the UserID. If you need to, you can add caching of retrieval of info, but that should be in a caching layer separate from sessions.
2 - Use database sessions, so that there is no concern about server memory, and so that you can add more web servers easily if desired (note, StateServer gives you this capability as well). Additionally, this lets you recycle the app pool without users losing their session. This is the main reason I do it - it lets me deploy on the fly.
The reason to treat sessions so delicately, is that they hang around after a user's last request, typically for 10 to 20 minutes. So, requests from many different sessions can use up large amounts of server memory if you are storing large objects in session. Doing even crazier things, like storing database connections in session, can result in you using up all available database connections, just because so many are hanging around in memory waiting for the session to expire.
Ideally, there is no "managing" of sessions required.
What I normally do is only keep the id of an object in Session and keep the data in a caching layer in front of the database. That way, you keep your session fairly small but most data can still be retrieved efficiently if the item is in cache. This has usually worked well for me.
I think you're misunderstanding sessions in ASP.NET. You do not explicitly manage them (although you could, you would rarely want to) they are implicitly created and destroyed by ASP.NET and IIS. Every user is given a session.
Now anything you store inside a users session is stored in memory on the server so if you routinely place large objects in each users sessions this will cause the memory to grow rapidly leading to performance and scalability issues.
The only data you need to store in the session is transient data that you want to be available across different page requests that you want to be secured that it is not available to the client in a cookie or similar construct.
However reading what you're doing this is most likely not needed. Since you are working with a specific page and "director" functionality this would most likely be fitting to place on the page in the viewstate as an intermediary data store.
There is no wright answer. It depends on the number of concurrent users, on the size of your memory.
Keeping things on session is faster than on a database, if you have your session variables in proc.
I usually try to keep just things that I know the user might need during a specific task, or, like you said, information of the current user.
My suggestion is:
Wrap the session access into a IPerSessionCache interface
Have a concrete implementation of this interface
Use an instace of the concrete class in the code to get/set session data
By this, then you have the freedom to clean up the instance of IPerSessionCache when the session ends
The code will not need to know where the IPerSessionCache actually stores the data (ie. it could in the Session or in a database or in a cookie etc.)
You would have better control on the lifetime of the IPerSessionCache especially if you use a DI container for managing & creating the instance
HTH.
My suggestion: Don't use session variables. Store everything in a database.

Caching database data in session - getting the balance right

If you cache data from your database in ASP.NET session then you will speed up subsequent requests for that data (note: depending on the serialization/deserialization cost of the data concerned), at the expense of memory load in IIS.
(OK, this is probably a simplification of the reality of the situation - feel free to correct or refine my statement above)
Do you use any rules (simple rules of thumb or otherwise) to decide when it is appropriate to cache in Session?
Update
Can use of Session for storing read-only data without very well thought out rationale simply be considered another case of Premature Optimization? (and therefore, bad)
Keep in mind with caching in session if you use the InProc mode, you are limiting your scalability unless your code will go back to the DB when the session is empty. You will be forced to use a single server or pin your user to a particular server.
If your using Session State Server this doesn't apply, and if your using SQL to store session state then using session for caching is pointless.
Edit
Any advice around this topic is subject to your specific environment. As you stated, a very complex sql query might benefit from being cached even when using SQL Session State. I think the most important thing is first make your application perform the functions and achieve the business requirements. Then go back and test and optimize the application to handle the load you expect.
Edit 2
Based on your update no I don't think this is true. In one of my ASP.Net Applications I am using session state to store a complex object model that the user is then manipulating and modifying. We are using AJAX and so we have a lot of short communication to server as user updates the object. Keeping the object in session was done as a convience. The object performs a lot of customized calculations to generate different data points, so doing this on the server was ideal as opposed to trying to replicate the code in the servr and in javascript. Also keeping it in session lets us have an undo function very easily.
And Yes I know we sacrificed scalability, but I welcome the day when I sit down with my boss and explain we have a problem because we have too many users (And I know he would too).
So I think the question is why are you storing data in session is it for convience and to provide access to transient data while the user is logged in? That is different then storing it there for caching. One thing to remeber with caching is how are you going to flush the cache? How do you invalidate it? I don't session state is built to handle this.
Edit 3
Back at it well read only data, user specific, expensive to load from DB, go ahead cache it in session. You can write your code so if it's not in session then you hit the DB. Have a nice little helper class that does this hidding it from your web app that your even using session and you should be good. Nice thing about hidding where you store it from your web if you find you run into issues you only have one place to change it.
Given that it is very hard to guarantee that data in a session gets cleared up in a reasonable time frame, I would be very wary of cashing anything more than the odd integer there. When you get up to a large scale you will either hit memory issues or have all your users get session time out errors. Remember that unlike the Cache object sessions don't get killed off when memory is low.
Also as Josh says if you go to multiple servers and need to use either a Database or Session State Server your session object will need to be serialisable. In this case the cost of serialising and de serialising is likely to be worse than the cost of a well optimized query.

What to put in a session variable

I recently came across a ASP 1.1 web application that put a whole heap of stuff in the session variable - including all the DB data objects and even the DB connection object. It ends up being huge. When the web session times out (four hours after the user has finished using the application) sometimes their database transactions get rolled back. I'm assuming this is because the DB connection is not being closed properly when IIS kills the session.
Anyway, my question is what should be in the session variable? Clearly some things need to be in there. The user selects which plan they want to edit on the main screen, so the plan id goes into the session variable. Is it better to try and reduce the load on the DB by storing all the details about the user (and their manager etc.) and the plan they are editing in the session variable or should I try to minimise the stuff in the session variable and query the DB for everything I need in the Page_Load event?
This is pretty hard to answer because it's so application-specific, but here are a few guidelines I use:
Put as little as possible in the session.
User-specific selections that should only last during a given visit are a good choice
often, variables that need to be accessible to multiple pages throughout the user's visit to your site (to avoid passing them from page to page) are also good to put in the session.
From what little you've said about your application, I'd probably select your data from the db and try to find ways to minimize the impact of those queries instead of loading down the session.
Do not put database connection information in the session.
As far as caching, I'd avoid using the session for caching if possible -- you'll run into issues where someone else changes the data a user is using, plus you can't share the cached data between users. Use the ASP.NET Cache, or some other caching utility (like Memcached or Velocity).
As far as what should go in the session, anything that applies to all browser windows a user has open to your site (login, security settings, etc.) should be in the session. Things like what object is being viewed/edited should really be GET/POST variables passed around between the screens so a user can use multiple browser windows to work with your application (unless you'd like to prevent that).
DO NOT put UI objects in session.
beyond that, i'd say it varies. too much in session can slow you down if you aren't using the in process session because you are going to be serializing a lot + the speed of the provider. Cache and Session should be used sparingly and carefully. Don't just put in session because you can or is convenient. Sit down and analyze if it makes sense.
Ideally, the session in ASP should store the least amount of data that you can get away with. Storing a reference to any object that is holding system resources open (particularly a database connection) is a definite scalability killer. Also, storing uncommitted data in a session variable is just a bad idea in most cases. Overall it sounds like the current implementation is abusively using session objects to try and simulate a stateful application in a supposedly stateless environment.
Although it is much maligned, the ASP.NET model of managing state automatically through hidden fields should really eliminate the majority of the need to keep anything in session variables.
My rule of thumb is that the more scalable (in terms of users/hits) that the app needs to be, the less you can get away with using session state. There is, however, a trade-off. For web applications where the user is repeatedly accessing the same data and typically has a fairly long session per use of the site, some caching (if necessary in session objects) can actually help scalability by reducing the load on the DB server. The idea here is that it is much cheaper and less complex to farm the presentation layer than the back-end DB. Of course, with all things, this advice should be taken in moderation and doesn't apply in all situations, but for a fairly simple in-house CRUD app, it should serve you well.
A very similar question was asked regarding PHP sessions earlier. Basically, Sessions are a great place to store user-specific data that you need to access across several page loads. Sessions are NOT a great place to store database connection references; you'd be better to use some sort of connection pooling software or open/close your connection on each page load. As far as caching data in the session, this depends on how session data is being stored, how much security you need, and whether or not the data is specific to the user. A better bet would be to use something else for caching data.
storing navigation cues in sessions is tricky. The same user can have multiple windows open and then changes get propagated in a confusing manner. DB connections should definitely not be stored. ASP.NET maintains the connection pool for you, no need to resort to your own sorcery. If you need to cache stuff for short periods and the data set size is relatively small, look into ViewState as a possible option (at the cost of loading more bulk onto the page size)
A: Data that is only relative to one user. IE: a username, a user ID. At most an object representing a user. Sometimes URL-relative data (like where to take somebody) or an error message stack are useful to push into the session.
If you want to share stuff potentially between different users, use the Application store or the Cache. They're far superior.
Stephen,
Do you work for a company that starts with "I", that has a website that starts with "BC"? That sounds exactly like what I did when I first started developing in .net (and was young and stupid) -- I crammed everything I could think of in session and application. Needless to say, that was double-plus ungood.
In general, eschew session as much as possible. Certainly, non-serializable objects shouldn't be stored there (database connections and such), but even big, serializable objects shouldn't be either. You just don't want the overhead.
I would always keep very little information in session. Sessions use server memory resources which is expensive. Saving too many values in session increases the load on server and eventualy the performance of the site will go down. When you use load balance servers, usage of session can run into problems. So what I do is use minimal or no sessions, use cookies if the information is not very critical, use hidden fields more and database sessions.

ASP.Net Session

I am wanting to store the "state" of some actions the user is performing in a series of different ASP.Net webforms. What are my choices for persisting state, and what are the pros/cons of each solution?
I have been using Session objects, and using some helper methods to strongly type the objects:
public static Account GetCurrentAccount(HttpSessionState session)
{
return (Account)session[ACCOUNT];
}
public static void SetCurrentAccount(Account obj, HttpSessionState session)
{
session[ACCOUNT] = obj;
}
I have been told by numerous sources that "Session is evil", so that is really the root cause of this question. I want to know what you think "best practice", and why.
There is nothing inherently evil with session state.
There are a couple of things to keep in mind that might bite you though:
If the user presses the browser back button you go back to the previous page but your session state is not reverted. So your CurrentAccount might not be what it originally was on the page.
ASP.NET processes can get recycled by IIS. When that happens you next request will start a new process. If you are using in process session state, the default, it will be gone :-(
Session can also timeout with the same result if the user isn't active for some time. This defaults to 20 minutes so a nice lunch will do it.
Using out of process session state requires all objects stored in session state to be serializable.
If the user opens a second browser window he will expect to have a second and distinct application but the session state is most likely going to be shared between to two. So changing the CurrentAccount in one browser window will do the same in the other.
Your two choices for temporarily storing form data are, first, to store each form's information in session state variable(s) and, second, to pass the form information along using URL parameters. Using Cookies as a potential third option is simply not workable for the simple reason that many of your visitors are likely to have cookies turned off (this doesn't affect session cookies, however). Also, I am assuming by the nature of your question that you do not want to store this information in a database table until it is fully committed.
Using Session variable(s) is the classic solution to this problem but it does suffer from a few drawbacks. Among these are (1) large amounts of data can use up server RAM if you are using inproc session management, (2) sharing session variables across multiple servers in a server farm requires additional considerations, and (3) a professionally-designed app must guard against session expiration (don't just cast a session variable and use it - if the session has expired the cast will throw an error). However, for the vast majority of applications, session variables are unquestionably the way to go.
The alternative is to pass each form's information along in the URL. The primary problem with this approach is that you'll have to be extremely careful about "passing along" information. For example, if you are collecting information in four pages, you would need to collect information in the first, pass it in the URL to the second page where you must store it in that page's viewstate. Then, when calling the third page, you'll collect form data from the second page plus the viewstate variables and encode both in the URL, etc. If you have five or more pages or if the visitor will be jumping around the site, you'll have a real mess on your hands. Keep in mind also that all information will need to A) be serialized to a URL-safe string and B) encoded in such a manner as to prevent simple URL-based hacks (e.g. if you put the price in clear-text and pass it along, someone could change the price). Note that you can reduce some of these problems by creating a kind of "session manager" and have it manage the URL strings for you but you would still have to be extremely sensitive to the possibility that any given link could blow away someone's entire session if it isn't managed properly.
In the end, I use URL variables only for passing along very limited data from one page to the next (e.g. an item's ID as encoded in a link to that item).
Let us assume, then, that you would indeed manage a user's data using the built-in Sessions capability. Why would someone tell you that "Session is evil"? Well, in addition to the memory load, server-farm, and expiration considerations presented above, the primary critique of Session variables that they are, effectively, untyped variables.
Fortunately, prudent use of Session variables can avoid memory problems (big items should be kept in the database anyhow) and if you are running a site large enough to need a server farm, there are plenty of mechanisms available for sharing state built in to ASP.NET (hint: you will not use inproc storage).
To avoid essentially all of the rest of Session's drawbacks, I recommend that implement an object to hold your session data as well as some simple Session object management capabilities. Then build these into a descendent of the Page class and use this descendent Page class for all of your pages. It is then a simple matter to access your Session data via the page class as a set of strongly-typed values. Note that your Object's fields will give you a way to access each of your "session variables" in a strongly typed manner (e.g. one field per variable).
Let me know if this is a straightforward task for you or if you'd like some sample code!
As far as I know, Session is the intended way of storing this information. Please keep in mind that session state generally is stored in the process by default. If you have multiple web servers, or if there is an IIS reboot, you lose session state. This can be fixed by using a ASP.NET State Service, or even an SQL database to store sessions. This ensures people get their session back, even if they are rerouted to a different web server, or in case of a recycle of the worker process.
One of the reasons for its sinister reputation is that hurried developers overuse it with string literals in UI code (rather than a helper class like yours) as the item keys, and end up with a big bag of untestable promiscuous state. Some sort of wrapper is an entry-level requirement for non-evil session use.
As for "Session being evil" ... if you were developing in classic ASP I would have to agree, but ASP.NET/IIS does a much better job.
The real question is what is the best way to maintain state. In our case, when it comes to the current logged in user, we store that object in Session, as we are constantly referring to it for their name, email address, authorization and so forth.
Other little tidbits of information that doesn't need any long-term persistence we use a combination of cookies and viewstate.
When you want to store information that can be accessed globally in your web application, a way of doing this is the ThreadStatic attribute. This turns a static member of a Class into a member that is shared by the current thread, but not other threads. The advantage of ThreadStatic is that you don't have to have a web context available. For instance, if you have a back end that does not reference System.Web, but want to share information there as well, you can set the user's id at the beginning of every request in the ThreadStatic property, and reference it in your dependency without the need of having access to the Session object.
Because it is static but only to a single thread, we ensure that other simultaneous visitors don't get our session. This works, as long as you ensure that the property is reset for every request. This makes it an ideal companion to cookies.
I think using Session object is OK in this case, but you should remember Session can expire if there is no browser activity for long time (HttpSessionState.Timeout property determines in how many minutes session-state provider terminates the session), so it's better to check for value existence before return:
public static Account GetCurrentAccount(HttpSessionState session)
{
if (Session[ACCOUNT]!=null)
return (Account)Session[ACCOUNT];
else
throw new Exception("Can't get current account. Session expired.");
}
http://www.tigraine.at/2008/07/17/session-handling-in-aspnet/
hope this helps.
Short term information, that only needs to live until the next request, can also be stored in the ViewState. This means that objects are serialized and stored in the page sent to the browser, which is then posted back to the server on a click event or similar. Then the ViewState is decoded and turned into objects again, ready to be retrieved.
Sessions are not evil, they serve an important function in ASP.NET application, serving data that must be shared between multiple pages during a user's "session". There are some suggestions, I would say to use SQL Session management when ever possible, and make certain that the objects you are using in your session collection are "serializable". The best practices would be to use the session object when you absolutely need to share state information across pages, and don't use it when you don't need to. The information is not going to be available client side, A session key is kept either in a cookie, or through the query string, or using other methods depending on how it is configured, and then the session objects are available in the database table (unless you use InProc, in which case your sessions will have the chance of being blown away during a reload of the site, or will be rendered almost useless in most clustered environments).
I think the "evil" comes from over-using the session. If you just stick anything and everything in it (like using global variables for everything) you will end up having poor performance and just a mess.
Anything you put in the session object stays there for the duration of the session unless it is cleaned up. Poor management of memory stored using inproc and stateserver will force you to scale out earlier than necessary. Store only an ID for the session/user in the session and load what is needed into the cache object on demand using a helper class. That way you can fine tune it's lifetime according to how often that data us used. The next version of asp.net may have a distributed cache(rumor).
Session as evil: Not in ASP.NET, properly configured. Yes, it's ideal to be as stateless as possible, but the reality is that you can't get there from here. You can, however, make Session behave in ways that lessen its impact -- Notably StateServer or database sessions.

Resources