I will host my ASP.NET MVC4 app as a redundant Azure app. During a session, the app performs computationally expensive operations that produce non-serializable objects. Creation of the objects is repeatable; I could perform the expensive operation each time I need the object, but I would prefer to just do it the first time and save the object for later reuse.
I want to use the standard distributed session state mechanism in Azure for storing the usual session state info, but that mechanism requires that session data be serializable. Is there another mechanism I can use to cache the expensive-to-create, non-serializable objects?
Bob
All distributed cache services provided by Windows Azure need serialization currently, not only the shared cache, but dedicate/co-located cache as well.
But it's not necessary to serialize if you are going to use in memory cache. But this is not good for scaling-out, and you may not be able to have azure SLA if you've only 1 instance.
So my suggestion is to optimize your serialization and try to use azure cache.
Do these objects have to be stored in centralized storage or can you store them in the "InProc" session state?
If not, I'm afraid you'll need to serialize them into something (either SQL Azure, file, app-fabric cache, etc).
So either find a way to serialize them into something persistable or store them in RAM, with an extra copy on every web server
Related
Recently, I have been learning how to use Session State in ASP.NET Core and it has worked fine when my application is running on a single instance.
As my Session backing store, I have been using the Microsoft.Extensions.Caching.Cosmos package and have been accessing the Session via HttpContext.Session in controllers, or, via HttpContextAccessor in middleware and/or custom components.
I was reading the State management article on MSFT and came across:
A better approach is to use a Redis or SQL Server distributed cache, which doesn't require sticky sessions. For more information, see Distributed caching in ASP.NET Core.
I see that I am able to use the IDistributedCache interface as shown here but now I am confused.
I assume that the way I was doing it before was working only with 1 instance because I was perhaps doing it incorrectly? As I mentioned, as soon as I go to multiple app instances, the application breaks.
Am I supposed to use this interface directly to get the functionality of the cache? How is my Session still associated with the cache then?
Here is how I was doing it originally:
if (string.IsNullOrEmpty(context.Session.GetString(SessionKeys.MyTestKey)))
{
Redirect('/');
return;
}
Versus how I am planning on doing it moving forward:
if (string.IsNullOrEmpty(cache.GetString(SessionKeys.MyTestKey)))
{
Redirect('/');
return;
}
As far as I know, the session will use the distributed cache to store the data. That means it could store and get the data directly to the distributed cache like redis or else when you add session or get the session.
When you want to get the session inside the application, the session library will directly read the data from the distributed cache.
For just one instance, if you read the session it may read it from cookie or your server's memory.
For multiple instance, all the application will read the session from same distributed cache, this makes your session working well.
So if you have set the distributed cache and right setting for the session, it could work well for multiple instance environment.
I have an ASP.net application that I'm moving to Azure. In the application, there's a query that joins 9 tables to produce a user record. Each record is then serialized in json and sent back and forth with the client. To increase query performance, the first time the 9 queries run and the record is serialized in json, the resulting string is saved to a table called JsonUserCache. The table only has 2 columns: JsonUserRecordID (that's unique) and JsonRecord. Each time a user record is requested from the client, the JsonUserCache table is queried first to avoid having to do the query with the 9 joins. When the user logs off, the records he created in the JsonUserCache are deleted.
The table JsonUserCache is SQL Server. I could simply leave everything as is but I'm wondering if there's a better way. I'm thinking about creating a simple dictionary that'll store the key/values and put that dictionary in AppFabric. I'm also considering using a NoSQL provider and if there's an option for Azure or if I should just stick to a dictionary in AppFabric. Or, is there another alternative?
Thanks for your suggestions.
"There are only two hard problems in Computer Science: cache invalidation and naming things."
Phil Karlton
You are clearly talking about a cache and as a general principle, you should not persist any cached data (in SQL or anywhere else) as you have the problem of expiring the cache and having to do the deletes (as you currently are). If you insist on storing your result somewhere and don't mind the clearing up afterwards, then look at putting it in an Azure blob - this is easily accessible from the browser and doesn't require that the request be handled by your own application.
To implement it as a traditional cache, look at these options.
Use out of the box ASP.NET caching, where you cache in memory on the web role. This means that your join will be re-run on every instance that the user goes to, but depending on the number of instances and the duration of the average session may be the simplest to implement.
Use AppFabric Cache. This is an extra API to learn and has additional costs which may get quite high if you have lots of unique visitors.
Use a specialised distributed cache such as Memcached. This has the added cost/hassle of having to run it all yourself, but gives you lots of flexibility in the long run.
Edit: All are RAM based. Using ASP.NET caching is simpler to implement and is faster to retrieve the data from cache because it is on the same machine - BUT requires the cache to be populated for each instance of the web role (i.e. it is not distributed). AppFabric caching is distributed but is also a bit slower (network latency) and, depending what you mean by scalable, AppFabric caching currently behaves a bit erratically at scale - so make sure you run tests. If you want scalable, feature rich distributed caching, and it is a big part of your application, go and put in Memcached.
Until now, I was under impression that where to store your session is just a configuration switch and should not require any code change. I was having misconception that moving from inproc to state management service or database should not require any code change.
My misconception was broken when I realized that the object stored in the session needs to be serializable if they need to work with state management service or database. That means, I need to mark all those objects which I plan to put in session as serializable.
How can I make it completely flexible for the deployers to decide on the session storage. What all other things (along with serialization) I need to take care to make it work with any session storage option.
Thanks.
Update
The following code should work in inproc mode but, I guess, will not work with state management or database session storage option.
UserPreferences preferences = Session["UserPreferences"]; //UserPreferences in serializable
preference.Autologout = true; //I believe, this will only work with inproc. The updated value will not reflect in database.
The above session handling pattern is common. How to catch such issues while coding/compilation and not at run-time. Are there static code analysis rules available for appropriate session handling.
I've read several threads about this topic and need some clarification on a few sentences I read in a book:
If you store your Session state in-process, your application is not scalable. The reason for this is that the Session object is stored on one particular server. Therefore storing Session state in-process will not work with a web farm.
What does "scalable" in the first sentence mean?
Does the third sentence means if my app resides on a shared web host, I shouldn't use Session["myData"] to store my stuff? If so, what should I use?
Thanks.
1:
Scalability in this sense:
the ability of a system, network, or process, to handle growing amounts of work in a graceful manner or its ability to be enlarged to accommodate that growth.[
2:
Use a session server or store sessions in SQL Server, which are described here.
ASP.NET can store all the combined Session information for an Application (the "Session State") in 3 possible places on the server-side (client cookies is also possible but that is a different story):
"InProc" (In Process) which means in memory on the IIS server attached to the asp.net worker process,
"StateServer" which is a separate process that can be accessed by multiple IIS servers but still stores the Session state in memory, and
"SQLServer" which stores the Session state in a SQL Server database.
1) The reason In-process is not scalable is if your needs exceed the capacity of a single IIS server, multiple servers can't use an In-process session state. If you have determined a shared hosting scenario will fulfill you needs, you don't need to worry about it.
2) When you store something in Session["Name"], ASP.net stores that data wherever the application is configured to store Session state. If you want to change where Session state is stored, all you need to do is configure your web.config file. If you are using a shared hosting environment, your IIS deployment is considered single server even though no doubt the actual servers are in a farm of some sort.
See: MSDN Session-State Modes
I am using ASP.NET's data caching API. For example:
HttpRuntime.Cache.Insert(my_data, my_key);
Is there any way to configure cache so its contents are preserved when the App Domain recycles?
I load many object into cache, but there is a substantial delay re-loading these every time the app domain restarts. Assume for this question that I can't prevent the appdomain restart due to a server configuration.
Is there any way to configure cache so
its contents are preserved when the
App Domain recycles?
No. The Cache object holds references in RAM. Period.
Alternatives:
Out-of-process Session state (although that's per-user)
Distributed cache
Use SQL Server as a cache (where it keeps data in memory, rather than on disk)
Write the objects to disk at the web tier
I generally prefer #3 myself, although there are scenarios where the others are appropriate.
Recycling the appdomain dumps the cache. If you want to get around this you'd need to use a distributed cache. Here's an example.
For your most expensive data you can cache the objects with a distributed cache such as Memcached or velocity. Depending on the size of the object and the length of runtime you could also serialize it to disk or to your database provided that the access speed to these resources is less than the time to create the object.
Now since the in-proc cache is super fast compared to any other solution since it just holds a reference to the memory object you will want to use it where possible. You can keep a copy of your cached object on disk until it is lost and then re-create it from that and place it in memory. Where this is tricky is when you have to expire the data so only use the cache/disk/sql combo where you won't need to expire/invalidate the data otherwise you will need to ensure that you clear both. This will also get around not being able to implement distributed caching on a shared server for example.