Default duration of Cache.Insert in ASP.NET - 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.

Related

Session compression. Negative and positive sides

In web.config you can enable session compression.
<sessionState mode="InProc" customProvider="DefaultSessionProvider" compressionEnabled="true" >
What are positive and negative sides of this action?
Well, on the positive side, you need less space.
On the negative side, it needs time to compress, so it's slower.
Let me add, that in my opinion, if you use sessions at all, you've made an architectural mistake (exceptions my apply to this rule, but very very rarely).
It's not a good idea, because if a page writes something in a session, this gets overwritten if I simultanously open the same page in another browser window (it's the same session).
And because InProc sessions expire when you change something in the web.config file, you can create unlimited number of bugs for EVERY currently active user...
Plus you loose inProc sessions, if the VM gets moved to another server (cloud environments, failover, dynamic scaleOut).
Also, the InProc provider doesn't require objects to be marked as serializable.
If you change to, for example, an SQL session provider, you'll get exceptions in all places where you put an object that hasn't been marked as serializable into the session.
For example, when you need to query all the locations a user may access (according to portofolio rights in T_SYS_LocationRights):
You get the UserID from the formsAuth-cookie, then use it as the parameter:
DECLARE #userID integer
SET #userID = 12435
SELECT * FROM T_Locations
WHERE (1=1)
AND
(
(
SELECT ISNULL(MAX(CAST(T_SYS_LocationRights.LR_IsRead AS integer)), 0)
FROM T_SYS_LocationRights
INNER JOIN T_User_Groups
ON T_User_Groups.USRGRP_GRP = T_SYS_LocationRights.LR_GRANTEE_ID
WHERE T_SYS_LocationRights.LR_LC_UID = T_Locations.LC_UID
AND T_User_Groups.USRGRP_USR = #userID
) = 1
)
Don't just query something after the maxim:
if you'll ever need it, it's already there.
Design a web-application (which is multi-threaded by design) after that maxim, is a very bad idea.
If you don't need it, don't query it.
If you need it, query it.
If you needed it, don't store it in the session, it's better to query it again, if necessary
You can win much more time by executing all database operations at once, get all the data you need into a System.Data.DataSet (in one query-operation, one connection open-and-close), and then use that. When the page reloads, you can always reload the data (as a matter of fact, you even should).
Don't use the session as cache. It's not the cache

ASP.NET session object lifetime pessimistic assumption !

I check a session object and if it does exist then call another method which would use that object indirectly. Although the second method would access this object in a few nanoseconds I was thinking of a situation when the object exactly expires between two calls. Does Session object extends its lifetime on every read access from code for preventing such a problem ? If not how to solve the problem ?
If you are going to say why I don't pass the retrieved object from first method to second one, this is because I pass the ASP.NET Page object which carries many other parameters inside it to second method and if I try to pass each of them separately, there would be many parameters while I just pass one Page object now.
Don't worry, this won't happen
If I understand your situation it works sort of this way:
Access a certain page
If session is active it immediately redirects to the second page or executes a certain method on the first page.
Second page/method uses session
You're afraid that session will expire between execution of the first and second method/page.
Basically this is impossible since your session timer was reset when just before the first page starts processing. So if the first page had active session then your second page/method will have it as well (as long as processing finishes before 20 minutes - default session timeout duration).
How is Session processed
Session is processed by means of an HTTP Module that runs on every request and before page starts processing. This explains the behaviour. If you're not familiar with HTTP Modules, then I suggest you read a bit about IHttpModule interface.
It's quite difficult to understand your question, IMHO, but I will try.
From what I understand, you're doing something like:
string helloWorld = string.Empty;
if (this.Session["myObject"] == null)
{
// The object was removed from the session or the session expired.
helloWorld = this.CreateNewMyObject();
}
else
{
// Session still exists.
helloWorld = this.Session["myObject"].ToString(); // <- What if the session expired just now?
}
or
// What if the session existed here...
if (this.Session["myObject"] == null)
{
this.Session["myObject"] = this.CreateNewMyObject();
}
// ... but expired just there?
string helloWorld = this.Session["myObject"].ToString();
I thought that Session object is managed by the same thread as the page request, which would mean that it is safe to check if object exists, than use it without a try/catch.
I were wrong:
For Cache objects you have to be aware of the fact that you’re dealing essentially with an object accessed across multiple threads
Source: ASP.NET Cache and Session State Storage
I were also wrong about not reading to carefully the answer by Robert Koritnik, which, in fact, clearly answers the question.
In fact, you are warned about the fact that an object might be removed during page request. But since Session lifespan relies on page requests, it would mean that you must take in account the removal of session variables only if your request takes longer than the session timeout (see How is Session processed in the answer by Robert Koritnik).
Of course, such situation is very rare. But if in your case, you are pretty sure that the page request can take longer than 20 minutes (default session timeout), than yes, you must take in account that an object may be removed after you've checked if it exists, but before you really use it.
In this situation, you can obviously increment the session timeout, or use try/catch when accessing the session objects. But IMHO, if the page request takes dozens of minutes, you must consider other alternatives, as Windows services, to do the work.
I'm having difficulties understanding what the problem here is but let me try it again referring to thread safety.
Thread safety issue
If this is a thread safety issue, you can always issue a lock when creating a certain session object so other parallel requests won't run into a problem by double creating your object.
if (obj == null)
{
lock (objLock)
{
if (obj == null)
{
obj = GenerateYourObject();
}
}
}
Check lock documentation on MSDN if you've never used it before. And don't forget to check other web resources as well.

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.

Httpruntime cache keys not unique?

Although i have specified a unique key, it seems the following code will return one value for 5 requests, then another for the next couple, then revert back to the value saved in the original request and just continue until there are 10's of different objects all stored under the same key.
It then seems almost random which of these values it will return from the cache.
string strDateTime = string.Empty;
string cachename = "datetimeexample";
object cachedobject = HttpRuntime.Cache.Get(cachename);
if (cachedobject != null)
strDateTime = (string)cachedobject;
else
{
strDateTime = DateTime.Now.ToString();
HttpRuntime.Cache.Insert(cachename, strDateTime, null, DateTime.MaxValue, TimeSpan.FromDays(10), CacheItemPriority.NotRemovable, null);
}
Response.Write(strDateTime +" keys:"+ HttpRuntime.Cache.Count);
Very confused, is this because of threading or something?
Ignoring the possibility of a server farm and load balancing, this behaviour can be caused by the application pool running as a web-garden. To quote the relevant section from MSDN:
Because Web gardens enable the use of
multiple processes, each process will
have its own copy of application
state, in-process session state,
caches, and static data. Web gardens
should not be used for all
applications, especially if they need
to maintain state. Be sure to
benchmark the performance of the
application before deciding whether
Web garden mode is appropriate.
This will cause it to appear as if caching is storing multiple values for the same key, effectively having duplicate entries in the cache.
To resolve this in IIS 7, open the application pool's Advanced Settings and set Maximum Worker Processes to 1. For IIS 6, see the MSDN article (With pretty screenshots).
Albeit 8 months late, I'm answering this question because I found it long before I found this decent article on web-garden gotchas. Hopefully this answer will save future searchers a chunk of time. :)
Your cachekey is always 'datetimeexample', therefore, you will always have one object in cache; and you will always receive that object back.
I am not quite sure what you are trying to accomplish here, as far as I'm concerned, this behaves exactly in the way it's supposed to do.

HttpContext.Cache Expiration

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.

Resources