HttpContext.Cache Expiration - asp.net

Is there a way to specify how long data is held in HttpContext.Cache?

You can specify it in the 4th parameter of Cache.Add():
public Object Add(
string key,
Object value,
CacheDependency dependencies,
DateTime absoluteExpiration, // After this DateTime, it will be removed from the cache
TimeSpan slidingExpiration,
CacheItemPriority priority,
CacheItemRemovedCallback onRemoveCallback
)
Edit:
If you access the cache via the indexer (i.e. Cache["Key"]), the method that is called uses no expiration and remains in the cache indefinitely.
Here is the code that is called when you use the indexer:
public void Insert(string key, object value)
{
this._cacheInternal.DoInsert(true, key, value, null, NoAbsoluteExpiration, NoSlidingExpiration, CacheItemPriority.Normal, null, true);
}

Use the Cache.Add method such as:-
HttpContext.Cache.Add("mykey", someObj, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 15, 0), CacheItemPriority.Normal, null);
The above expires in 15 minutes after the last time it was accessed. Alternative you can pass the Cache.NoSlidingExpiration to this parameter and use a specific DateTime in the previous parameter.

Yes there is a way to specify how long data is held in Cache, but none of the previous 2 examples would actually guaranty you'll keep your items in for the expected amount of time passed with either of the 2 time-based parameters of the Add method (absolute or sliding expiration).
The cache is just a cache and its purpose is to speed things up. So you should not expect it to hold onto your data and always be prepared to go fetch it if it's not there.
As you probably know you can have dependencies for the items and they'll expire based on that even if the time has not expired. This is an easy concept but there's another not that easy. The priority.
Based on the priority of your items and coupled with memory pressure, you can find yourself in a situation where you're caching data with good enough expiration times based on your calculations, but you don't get to use that data more than once making your cache just an overhead in such situation.
EDIT: Well I forgot to specify THE actual way to really keep an item in for the amount of time you need to, and that's a product of chosing the desired time-based expiration, no dependency at all, not manually removing it, and using the NotRemovable priority. This is also how inproc session state is internally kept in the httpruntime cache.

Related

distinguish between session timeout and session explicit (programmatic) invalidation

I have an HttpSessionListener. Is there a way, inside its sessionDestroyed method to distinguish between the following cases:
the session was destroyed because the session-timeout configured in the web.xml was exceeded
the session was destroyed programmatically by the the application calling HttpSession#invalidate
My use case is that I have a Single Sign On (SSO) arrangement between a number of applications and I want a global single sign off when one of the applications participating in the SSO arrangement explicitly logs off but not when its session times out, hence the need to distinguish between the two cases. I guess a way would be for the application to set some flag in the session object just prior to calling HttpSession#invalidate. The HttpSessionListener would then examine the session object and if that flag is found it would know this was a programmatic logout. If not, it was a container logout. Would that make sense and / or is there a better way?
You can use HttpSession#getLastAccessedTime() to obtain the timestamp of the last request sent by the client associated with the session. Then you can just do the math with help of HttpSession#getMaxInactiveInterval() and the current timestamp.
long lastAccessedTime = session.getLastAccessedTime();
long timeoutInMillis = TimeUnit.SECONDS.toMillis(session.getMaxInactiveInterval());
long now = System.currentTimeMillis();
boolean sessionHasBeenTimedout = (now - timeoutInMillis > lastAccessedTime);
// ...

Asp .net session variables update by thread is not getting reflected in Session

In my page1.aspx i am generating a report from database by using thread.
//on button click
Hashtable ht = (Hashtable)Session["ReportParam"];
ReportThreadClass rth = new ReportThreadClass(ht);
Thread thread = new System.Threading.ThreadStart(rth .Run);
thread.Start();
In my thread class's rum method i am updating values in Hashtable that how many pages i have created.
//in thread' method
public virtual void Run()
{
int pagecount=0;
while(done)
{
//loading data from DB and generating html pages
ht["Total_Pages"] = pagecount;
}
}
At my Page2.aspx i am reading values from Session Variable
Hashtable ht = (Hashtable)Session["ReportParam"];
int TotalPages = (int) ht["Total_Pages"];
When i run above code in InProc mode every thing is working fine i am getting updated values from session.
Because every thing is stored in static variable, and ht is referenced by Session so it automatically get updated in session (HashTable not needed to reassign it to session back).
But when i run code in State server (OutProc mode) It need to store session data in different process by Serializing Hash-table.
But the value of Total_Pages is not getting updated in Page2.aspx even after Thread run completely.
So is there any event or method which get fired to store all updates in session variable to State-Server , if yes then pls tell me . if not then pls suggest me some idea to get updated value in page2.aspx.
I would explictely SET and GET SessionState like so:
In your thread Run
// no complex object like hastable, just a plain value...
Session["pageCount"] = pageCount;
In your page2.apsx:
var pageCount = (int) Session["pageCount"]??0;
The reason your report thread is not updating it's session value when using out-of-proc sessionstate is because the session has no way to detect the hashtable has a changed value, therefor it doesn't update the underlying storew with the serialized version of the hastable. When you explicity store one immutable object it will persist one it's value changed;
As the session might already be gone when your thread finishes a begtter option is to get hold of a reference to SqlSessionStateStore and call SetAndReleaseItemExclusive. Ultimately you might want to have an overloaded SessionStateProvider that can handle your scenario.
In Out Proc Mode Session is saved after some event so if your thread is updating your session variables then it won't persist in storage.
If u are using Inproc Mode then session store in Static Dictionary so if your thread updating it, u will get updated value to any page.
So u have two solutions for this situation
Use inProc mode
Maintain a dictionary in your thread class with key as Session id and value is your hash-table, So if page2.aspx wants to read value of hash-table then it will pass his session id to method and which will return required value.
Less efficient but I'd probably just ping the database for the page count on Page2.
Or create a separate session value for the page count on Page1, at the same time as doing everything else. (EDIT: Nevermind the second part, that's what Rene suggested below).

Does any asp.net data cache support background population of cache entries?

We have a data driven ASP.NET website which has been written using the standard pattern for data caching (adapted here from MSDN):
public DataTable GetData()
{
string key = "DataTable";
object item = Cache[key] as DataTable;
if((item == null)
{
item = GetDataFromSQL();
Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300), TimeSpan.Zero;
}
return (DataTable)item;
}
The trouble with this is that the call to GetDataFromSQL() is expensive and the use of the site is fairly high. So every five minutes, when the cache drops, the site becomes very 'sticky' while a lot of requests are waiting for the new data to be retrieved.
What we really want to happen is for the old data to remain current while new data is periodically reloaded in the background. (The fact that someone might therefore see data that is six minutes old isn't a big issue - the data isn't that time sensitive). This is something that I can write myself, but it would be useful to know if any alternative caching engines (I know names like Velocity, memcache) support this kind of scenario. Or am I missing some obvious trick with the standard ASP.NET data cache?
You should be able to use the CacheItemUpdateCallback delegate which is the 6th parameter which is the 4th overload for Insert using ASP.NET Cache:
Cache.Insert(key, value, dependancy, absoluteExpiration,
slidingExpiration, onUpdateCallback);
The following should work:
Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300),
Cache.NoSlidingExpiration, itemUpdateCallback);
private void itemUpdateCallback(string key, CacheItemUpdateReason reason,
out object value, out CacheDependency dependency, out DateTime expiriation,
out TimeSpan slidingExpiration)
{
// do your SQL call here and store it in 'value'
expiriation = DateTime.Now.AddSeconds(300);
value = FunctionToGetYourData();
}
From MSDN:
When an object expires in the cache,
ASP.NET calls the
CacheItemUpdateCallback method with
the key for the cache item and the
reason you might want to update the
item. The remaining parameters of this
method are out parameters. You supply
the new cached item and optional
expiration and dependency values to
use when refreshing the cached item.
The update callback is not called if
the cached item is explicitly removed
by using a call to Remove().
If you want the cached item to be
removed from the cache, you must
return null in the expensiveObject
parameter. Otherwise, you return a
reference to the new cached data by
using the expensiveObject parameter.
If you do not specify expiration or
dependency values, the item will be
removed from the cache only when
memory is needed.
If the callback method throws an
exception, ASP.NET suppresses the
exception and removes the cached
value.
I haven't tested this so you might have to tinker with it a bit but it should give you the basic idea of what your trying to accomplish.
I can see that there's a potential solution to this using AppFabric (the cache formerly known as Velocity) in that it allows you to lock a cached item so it can be updated. While an item is locked, ordinary (non-locking) Get requests still work as normal and return the cache's current copy of the item.
Doing it this way would also allow you to separate out your GetDataFromSQL method to a different process, say a Windows Service, that runs every five minutes, which should alleviate your 'sticky' site.
Or...
Rather than just caching the data for five minutes at a time regardless, why not use a SqlCacheDependency object when you put the data into the cache, so that it'll only be refreshed when the data actually changes. That way you can cache the data for longer periods, so you get better performance, and you'll always be showing the up-to-date data.
(BTW, top tip for making your intention clearer when you're putting objects into the cache - the Cache has a NoSlidingExpiration (and a NoAbsoluteExpiration) constant available that's more readable than your Timespan.Zero)
First, put the date you actually need in a lean class (also known as POCO) instead of that DataTable hog.
Second, use cache and hash - so that when your time dependency expires you can spawn an async delegate to fetch new data but your old data is still safe in a separate hash table (not Dictionary - it's not safe for multi-reader single writer threading).
Depending on the kind of data and the time/budget to restructure SQL side you could potentially fetch only things that have LastWrite younger that your update window. you will need 2-step update (have to copy dats from the hash-kept opject into new object - stuff in hash is strictly read-only for any use or the hell will break loose).
Oh and SqlCacheDependency is notorious for being unreliable and can make your system break into mad updates.

Viewstate in a .ashx Handler?

I've got a handler (list.ashx for example) that has a method that retrieves a large dataset, then grabs only the records that will be shown on any given "page" of data. We are allowing the users to do sorting on these results. So, on any given page run, I will be retrieving a dataset that I just got a few seconds/minutes ago, but reordering them, or showing the next page of data, etc.
My point is that my dataset really hasn't changed. Normally, the dataset would be stuck into the viewstate of a page, but since I'm using a handler, I don't have that convenience. At least I don't think so.
So, what is a common way to store the viewstate associated with a current user's given page when using a handler? Is there a way to take the dataset, encode it somehow and send that back to the user, and then on the next call, pass it back and then rehydrate a dataset from those bits?
I don't think Session would be a good place to store it since we might have 1000 users all viewing different datasets of different data, and that could bring the server to its knees. At least I think so.
Does anyone have any experience with this kind of situation, and can you give me any advice?
In this situation I would use a cache with some type of user and query info as the key. The reason being is you say it is a large dataset. Right there is something you don't want to be pushing up and down the pipe constantly. Remember your server still has to received the data if it is in ViewState and handle it. I would do something like this which would cache it for a specific user and have a short expiry:
public DataSet GetSomeData(string user, string query, string sort)
{
// You could make the key just based on the query params but figured
// you would want the user in there as well.
// You could user just the user if you want to limit it to one cached item
// per user too.
string key = string.Format("{0}:{1}", user, query);
DataSet ds = HttpContext.Current.Cache[key] as DataSet;
if (ds == null)
{
// Need to reload or get the data
ds = LoadMyData(query);
// Now store it and make the expiry short so it doesn't bog up your server
// needlessly... worst case you have to retrieve it again because the data
// has expired.
HttpContext.Current.Cache.Insert(key, ds, null,
DateTime.UtcNow.AddMinutes(yourTimeout),
System.Web.Caching.Cache.NoSlidingExpiration);
}
// Perform the sort or leave as default sorting and return
return (string.IsNullOrEmpty(sort) ? ds : sortSortMyDataSet(ds, sort));
}
When you say 1000's of users, does that mean concurrent users? If your expiration time was 1 minute how many concurrent users would make that call in a minute and require sorting. I think offloading the data to something like similar to ViewState is just trading some cache memory for bandwidth and processing load of larget requests back and forth. The less you have to transmit back and forth the better in my opinion.
Why don't you implement a server side caching?
A I understand, you're retrieving a large amount of data and then returns only necessary records from this data to different clients. So you could use HttpContext.Current.Cache property for this.
E.g. a property which encapsulates a data retrieving logic (gets from the original data store with the first request, then puts to cache and gets from cache with every next request) could be used. In this case all the necessary data manipulations (paging, etc.) may be done much more quicker than retrieving a large amount of data with the each request.
In the case when clients have different data sources (mean each client have its own data source) the solution above may also be implemented. I suppose each client has at least identifier, so you could use different caches for different clients (client identifier as a part of cache key).
The best you could do is "grow your own" by including the serialized data set in the body of the request to the ASHX handler. Your handler would then check to see if the request does indeed have a body by checking Request.ContentLength and then reading from Request.InputStream, and if it does serializing that body back into the data set instead of reading from your database.

Default duration of Cache.Insert in ASP.NET

If I have the following line, when should I expect the cache to expire?
System.Web.HttpRuntime.Cache.Insert("someKey", "Test value");
"Never", that is, as soon as memory is low and ASP.NET Cache thinks it has something more important to keep.
This will insert the object without an explicit expiration set. This means the object will not automatically be removed from the cache, unless the runtime decides to remove stuff from the cache due to high memory usage.
Calling this overload is the same as calling
Cache.Insert(
key, value,
null, /*CacheDependency*/
NoAbsoluteExpiration, /*absoluteExpiration*/
NoSlidingExpiration, /*slidingExpiratioin*/
CacheItemPriority.Normal, /*priority*/
null /*onRemoveCallback*/
);
BTW: you can use .NET reflector to find out such things.

Resources