Changing frequency of ASP.NET cache item expiration? - asp.net

I noticed that the ASP.NET cache items are inspected (and possibly removed) every 20 seconds (and oddly enough each time at HH:MM:00, HH:MM:20 and HH:MM:40). I spent about 15 minutes looking how to change this parameter without any success. I also tried to set the following in web.config, but it did not help:
<cache privateBytesPollTime="00:00:05" />
I’m not trying to do anything crazy, but it would be nice if it was, say, 5 seconds instead of 20, or at least 10 for my application.

Poking around with Reflector reveals that the the interval is hardcoded. Expiry is handled by an internal CacheExpires class, whose static constructor contains
_tsPerBucket = new TimeSpan(0, 0, 20);
_tsPerBucket is readonly, so there can't be any configuration setting that modifies it later.
The timer that will trigger the check for expired items is then set up in CacheExpires.EnableExpirationTimer()...
DateTime utcNow = DateTime.UtcNow;
TimeSpan span = _tsPerBucket - new TimeSpan(utcNow.Ticks % _tsPerBucket.Ticks);
this._timer = new Timer(new TimerCallback(this.TimerCallback), null,
span.Ticks / 0x2710L, _tsPerBucket.Ticks / 0x2710L);
The calculation of span ensures that the timer fires exactly on :00, :20, :40 seconds, though I can't see any reason to bother. The method that the timer calls is internal, so I don't think there's any way to set up your own timer to call it more often (ignoring reflection).
However, the good news is that you shouldn't really have any reason to care about the interval. Cache.Get() checks that the item hasn't expired, and if it has then it removes the item from the cache immediately and returns null. Therefore you'll never get an expired item from the cache, even though expired items may stay in the cache for up to 20 seconds.

According to the documentation, privateBytesPollTime is for "worker process memory usage" and the default is 1 second. I don't think this relates to cache item removal.
I did confirm your results using an item removal callback- it looks like items are removed at the bottom of the minute, :20, and :40 seconds. This suggests that an item may remain in the cache for up to 20 seconds past the AbsoluteExpiration set on them. I couldn't find any documentation stating whether the 20 second polling interval could be changed.

Crazy, but working solution (all steps are needed):
// New value for cache expiration cycle
// System.Web.Caching.CacheExpires._tsPerBucket;
// Set 1 seconds instead of 20sec
const string assembly = "System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
var type = Type.GetType("System.Web.Caching.CacheExpires, " + assembly, true, true);
var field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic);
field.SetValue(null, TimeSpan.FromSeconds(1));
// Recreate cache
// HttpRuntime._theRuntime._cacheInternal = null;
// HttpRuntime._theRuntime._cachePublic = null;
type = typeof (HttpRuntime);
field = type.GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic);
var runtime = field.GetValue(null);
field = type.GetField("_cachePublic", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(runtime, null);
field = type.GetField("_cacheInternal", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(runtime, null);

Related

How to fix Maximum call stack size exceeded error in appmaker

I'm currently validating a date box widget to prevent filing after a 30 days grace period. The date validation was working but after the alert prompted it wasn't going down(I was stock in here even after a couple of clicks). Also the date box is not going null.
function checkDateValidate(widget) {
var form = app.pages.Newitem.descendants;
var otdate = form.otDateBox.value;
var date = new Date();
var pastdate = date.setDate(date.getDate() - 31);
if(otdate <= pastdate) {
alert('Date exceeds within 30 days grace period is invalid.');
form.otDateBox.value = null;
}
}
I expected to clear the date box widget.
This error sometimes happens because there is an infinite loop going on and this is your case. It is very important that you understand the difference between the onValueChange event handler and the onValueEdit event handler.
The onValueChange:
This script will run on the client whenever the value property of this widget changes. The widget can be referenced using parameter widget and the new value of the widget is stored in newValue.
The onValueEdit:
This script will run on the client whenever the value of this widget is edited by the user. The widget can be referenced using parameter widget and the new value of the widget is stored in newValue. Unlike onValueChange(), this runs only when a user changes the value of the widget; it won't run in response to bindings or when the value is set programmatically.
Why is this happening?
Since your logic is set on the onValueChange event handler, this will be triggered everytime the dateBox widget value is changed, even programmatically; Hence, form.otDateBox.value = null; is triggering the logic over and over again. The reason why it is being triggered over and over again is due to your comparison logic:
if(otdate <= pastdate)
Here, the value of otdate has become null wich when converted to number Number(null) the value is 0(zero). The value of pastdate is obviously a number greater than zero, eg 1555413900712. So obviously, zero is less than or equal to 1555413900712 and that is why you are triggering an infinite loop.
So, in summary, there is only one way to fix this. Set the logic inside the onValueEdit event handler instead of the onValueChange.

Get AbsoluteExpiration of HttpContext.Current.Cache

In my asp.net application I am setting cache like this;
const string key = "MyTestKey";
object value = true;
DateTime absoluteExpiration = DateTime.Now.AddMinutes(10);
TimeSpan slidingExpiration = Cache.NoSlidingExpiration;
const CacheItemPriority priority = CacheItemPriority.Default;
CacheItemRemovedCallback onRemoveCallback = null;
HttpContext.Current.Cache.Add(key, value, null, absoluteExpiration, slidingExpiration, priority, onRemoveCallback);
How can I retrieve back absoluteExpiration of a HttpContext.Current.Cache object on next request? Or can I get TimeSpan of how long remain for cache I set up?
You cannot retrieve the expiration or other CacheItem information for the item once it has been added to the System.Web.Caching.Cache, except when the item is being removed (either explicitly by calling Remove, or by cache expiration) or updated. The only methods that exist retrieve the object return the object only.
If you want more advanced Cache functionality, you'll need to look into using something like MemoryCache instead.

New item added to session on every request

I found this behaviour by accident, as I return the count of items in a session in an error message and found that some sessions had as many as 120 items in them (they should have 1!). On further investigation I found that every request seems to add an item into the session. They are all negative integers, like -710, -140 -528. I can't seem to see a pattern in what number comes up.
I have checked my code for any interactions with the Session object and as far as I can tell it is not me. I store one item in the session which is my own object which has a number of other properties on it. My session state is SQL server, and I am only serialising a certain set of values that need to be kept.
Has anyone seen anything like this or has any advice on where I can troubleshoot further?
Thank you in advance.
-- Edit, as requested - first where I count the items in the session - this is done in the page load event of my master page. I loop through so I could inspect using the debugger.
int itemCount = Session.Count;
for (int i = 0; i < itemCount; i++)
{
object o = Session[i];
}
-- here is where I add my custom object to the session. This is called at session start and in my master page. It runs on a "get, but if not there, create" principle.
HttpSessionState Session = HttpContext.Current.Session;
HttpRequest Request = HttpContext.Current.Request;
if (Session == null)
return null;
SessionData sessionData = (SessionData)Session[StaticNames.SESSION_NAME];
if (sessionData == null)
{
sessionData = new SessionData();
Session.Add(StaticNames.SESSION_NAME, sessionData);
}
I also have this to get the SessionData object from the session:
public SessionData(SerializationInfo info, StreamingContext ctxt)
{
this.IsManualLogin = (bool)info.GetValue("IsManualLogin", typeof(bool));
this.HasAskedUserForLocation = (bool)info.GetValue("HasAskedUserForLocation", typeof(bool));
// ... etc, more items for all users here
int? loginID = null;
try
{
loginID = info.GetInt32("LoginID");
}
catch
{
return;
}
this.LoginID = loginID.Value;
// ... etc, more items for logged in users only
}
There is also an equivalent method for adding this data to the SerializationInfo used for SqlSessionState.
Credit to the modest jadarnel27.
It turns out the Ajax Control Toolkit NoBot control adds an integer into your session on every request. My website has an auto 40 second refresh, similar to facebook, so this probably would have brought the whole thing crashing down at some point and I am lucky to find it now. Should anyone else consider using the NoBot control, be warned about this behaviour!

What will happen to objects in the cache with a timespan(0,0,0) expiration?

Our application adds objects to the cache using the following
int cacheTimeout = 5; // Default 5 minute timeout
if(ConfigurationManager.AppSettings["CacheTimeout"] != null)
{
cacheTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["CacheTimeout"].ToString());
}
_cache.Insert(Key, CacheItem, null, DateTime.MaxValue, new TimeSpan(0, cacheTimeout, 0));
Our manager is concerned about possible caching issues and wants to know, what happens if you insert an object with a 0 length time span.
I think the object will be immediately deleted. Right or wrong?
Answer is "Wrong".
When inserted into the cache with the absolute expiration set to NoAbsoluteExpiration and the sliding expiration set to new TimeSpan(0,0,0), the callback is not fired immediately. I didn't wait to see when it would fire, if ever - maybe it's 20 minutes, maybe it's never.
So changed the code to this:
int cacheTimeout = 5; // Default 5 minute timeout
if(ConfigurationManager.AppSettings["CacheTimeout"] != null)
{
cacheTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["CacheTimeout"].ToString());
}
TimeSpan expiration = cacheTimeout > 0 ? new TimeSpan(0,cacheTimeout,0) : new TimeSpan(0,0,1);
_cache.Insert(Key, CacheItem, null, System.Web.Caching.Cache.NoAbsoluteExpiration, expiration, CacheItemPriority.Default, OnUpdateCallback);
That way if we do have caching problems we can update web.config to set the cache time out to 0 and have a 1 second expiration.

Expiring a cached item manually/immediately

I'd like to place an item in the ASP.NET cache, say for instance a dataset, and not have it expire at all, until an event occurs in my application that requires the item to be refreshed. But until that happens, it should not expire, ever.
So a) is it possible to set a cached item never to expire (apart from setting expiry say 1 year in the future), and b) how does one manually force an item to expire?
Thanks!
I'm thinking this will work for you. Then when you're ready to refresh the item, you remove it form the cache manually and set it again...
Page.Cache.Add("object",
"something",
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback((s, o, r) =>
{
// some callback code if you want...
}));
UPDATED (better demo):
private int _counter = 0;
protected void Page_Load(object sender, EventArgs e)
{
// You can add the cache key like this
AddToCache("key", () => "some object " + _counter++);
//Any time you want to refresh the value, you can call RefreshCachedValue
RefreshCachedValue("key");
RefreshCachedValue("key");
RefreshCachedValue("key");
RefreshCachedValue("key");
RefreshCachedValue("key");
// In this demo, the cached value is now "some object 5"
}
private void AddToCache(string key, Func<object> getValueFunction)
{
Page.Cache.Add(key,
getValueFunction(),
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback((s, o, r) =>
{
AddToCache(s, getValueFunction);
}));
}
private void RefreshCachedValue(string key)
{
Page.Cache.Remove(key);
}
is it possible to set a cached item never to expire (apart from setting expiry say 1 year in the future)
Sort of - the Insert method has an overload that supports this: (reference)
DataSet myDataSet = getDataSet();
Page.Cache.Insert("MyDataSetCacheKey", myDataSet)
This will add the object to the cache with no sliding expiration and no absolute expiration, however it uses the default priority, not NotRemovable. If you wanted to force that, you'd have to write an extension method for Insert yourself.
how does one manually force an item to expire?
I'm assuming that you mean here 'I've cached this data forever, but now I want to change it'. In which case, you wouldn't expire it, you'd just remove it from the cache:
Page.Cache.Remove("MyDataSetCacheKey")
Logically, there's no difference between the item being removed from the cache because it expired, was flushed by the server trying to scavenge memory or you removing it manually.
There are some kinds of CacheDependency, you can use FileBased CacheDependency referring to an empty file.
If your data changes, just overwrite the file. As soon as the file changes, the cache will be reset.
http://msdn.microsoft.com/en-us/magazine/cc163955.aspx

Resources