How does SetVaryByCustom work? - asp.net

There are multiple ways to leverage ASP.Net MVC response/output caching. At the simplest you can cache a simple page that's the same for everyone:
[OutputCache(Duration=24*3600)] // cache 1 day
public ViewResult Index() ...
You can vary by specific params, you can bust the cache by a custom key. In all of those cases, the declarative OutputCacheAttribute is used to determine whether the page should just serve from cache. If it does serve from cache, the Action doesn't fire - CPU time saved.
So, suppose the Action accepts an Id, meaning its contents vary id to id. Suppose you want to bust the cache for specific Ids when their underlying data changes. MSDN says to set VaryByCustom inside the Action instead of declaratively in OutputCacheAttribute:
Response.Cache.SetVaryByCustom
Like:
[OutputCache(Duration=24*3600, VaryByParam="id")]
public async Task<ViewResult> Thing(string id)
{
Response.Cache.SetVaryByCustom("thing-" + id);
// Some big load of work we'd like to avoid when a ton of visitors hit
// the server goes here.
So... in every scenario until this one, that big load of work in the Action gets skipped if the page is valid in the cache. But it appears here it's not - unless SetVaryByCustom can interrupt the Action? How does this command work exactly?
If it doesn't interrupt the Action, is there some follow-on check I can do to see if the cache picked it up, so I can return early? And what would I return, given it's normally expecting a whole page filled with data?

Based on testing, it appears to work neither of the ways I proposed.
In my Action with this strategy applied, I:
Fire a log event
Fire SetVaryByCustom(id);
Fire another log event
And here's what I saw:
BrowserA: Visit a given id
Log: Both events fire - before and after
BrowserB: Visit same id
Log: (nothing)
BrowserA: Change id so an Invalidate fires for id
BrowserB: Visit id, sees 200
Log: Both events fire - before and after
BrowserA: Visit id, sees 200
Log: (nothing)
BrowserB: Visit id, sees 304
Log: (nothing)
In other words, the entire Action doesn't fire, just like in the static/declarative approach where it's all done in OutputCacheAttribute. What's pretty strange is each time it invalidates, the key gets an opportunity to change - you could pass a new key to SetVaryByCustom once per invalidation, but not more.
Unless you explicitly tell ASP.Net not to, the browser is also told to cache these pages, for the length of time remaining in the 24-hr period (via max-age). That means depending on how your visitors arrive, they may not see the page change as you intended. You can prevent this of course with Location=OutputCacheLocation.Server in your OutputCacheAttribute.
In any case, my core objective is in fact met - the server skips the CPU cost of the Action - just a bit more than I anticipated.

Related

Meteor - How do I automatically redirect user to page when data changes

I am writing a Meteor app that takes in external data from a machine (think IoT) and displays lots of charts, graphs, etc. So far so good. There are various pages in the application (one per graph type so far). Now as the data is being fed in "real-time", there is a situation (normal) where the data "set" gets totally reset. I.e. all the data is no longer valid. When this happens, I want to redirect the user back to the "Home" page regardless of where they are (well, except the home page).
I am hoping to make this a "global" item, but also don't want too much overhead. I noticed that iron:router (that I am using) has an onData()method but that seems a bit -- high overhead -- since it's just one piece of data that indicates a reset.
Since each page is rather "independent" and an user can stay on a page for a long time (the graphs auto-update as the underlying data changes) , I'm not even sure iron:router is the best approach at all.
This is Meteor 1.0.X BTW.
Just looking for a clean "proper" Meteor way to handle this. I could put a check in the redisplay logic of each page, but would think a more abstracted (read: global) approach would be more long-term friendly (so if we add more pages of graphs it automatically still works) ..
Thanks!
This is a job for cursor.observeChanges http://docs.meteor.com/#/full/observe_changes
Set up a collection that servers as a "reset notification" that broadcasts to all users when a new notification is inserted.
On Client:
criteria = {someCriteria: true};
query = ResetNotificationCollection.find(criteria)
var handle = query.observeChanges({
added: function (id, user) {
Router.go('home');
}
});
Whenever a reset happens:
notification = { time: new Date(), whateverYouWantHere: 'useful info' }
ResetNotificationCollection.insert notification
On insert, all clients observing changes on the collection will respond to an efficient little DDP message.

Make a final call to the Database when user leaves website (ASPX)?

I have a system set up to lock certain content in a database table so only one user can edit that content at a time. Easy enough and that part is working fine. But now I'm at a road block of how to send a request to "unlock" the content. I have the stored procedure to unlock the content, but how/where would I call it when the user just closes their browser?
You also can't know when the user turns off his computer. You have to do it the other way around.
Require that the lock be renewed periodically. Only the web site would do the periodic renewal. If the user stops using the web site, then the lock expires.
Otherwise, require the user to explicitly unlock the content. Other users who want to edit the content can then go yell at the first user when they can't do their jobs. Not a technological solution, but still a good one. Shame works.
The best thing you can really do is add something to your Session_End in your global.asax. Unfortunately, this won't fire until the session times out.
When the user clicks the "X" in their browser, there isn't anyway to guarantee the browser will send you anything back.
A quick note on the Session_End approaches. If you use this method, then you have to ensure
That sessionstate is InProc, eg. add something like this to your Web.config
<sessionState mode="InProc" timeout="timeout_in_minutes"/>
Make sure that you've setup IIS as to not recycle worker processes during normal operation (see for instance this blog post).
Edit:
Not directly answering the question directly, but another approach would be to use Optimistic concurrency control on the data in question.
There is such event as "user closes browser".
Nevertheless, I can think of two workarounds:
Use Javascript/Ajax to permanently
(lets say every 10 seconds) call a
method in your page. The DateTime of
your last query needs to be stored
somewhere. Now you write a windows
service that checks every second
which session are timed out. Perform
your custom action there.
Use the global.asax Session_End()
-Event. (cannot be used with every SessionState, look up for which ones
it is usable)
Trying to leave a stackoverflow answer page pops up an "are you sure" dialog. Perhaps during the on-page-leave event that SO uses (or however SO does this), you can send a final request with an XmlHttpRequest object. This won't cover if the browser process closes unexpectedly (use session_onend for that), but it will at least send the "I'm closed" event earlier
I think your one stored procedure can do the locking and unlocking (used with "Select #strNewMax As NewMax")...
Here is an example from a system I have:
Declare #strNewMax Char
Select #strNewMax = 'N'
BEGIN TRANSACTION
/* Lock only the rows for this Item ID, and hold those locks throughout the transaction. */
If #BidAmount > (Select Max(AB_Bid_AMT) from AuctionBid With(updlock, holdlock) Where AB_AI_ID = #AuctionItemId)
Begin
Insert Into AuctionBid (AB_AI_ID, AB_Bid_AMT, AB_Emp_ID, AB_Entry_DTM)
Select #AuctionItemId, #BidAmount, #EmployeeId, GetDate()
Select #strNewMax = 'Y'
End
COMMIT TRANSACTION
Select #strNewMax As NewMax
This will insert a record as the next highest bid, all while locking the entire table, so no other bids are processed at the same time. It will return either a 'Y' or 'N' depending on if it worked or not.
Maybe you can take this and adjust it to fit your application.

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.

how to display something one time every hour in asp.net?

how to display something one time every hour in asp.net ?
example for show messeage in Begining hour one time only?
i use for asp.net ajax timer control?
protected void Timer1_Tick(object sender, EventArgs e)
{
MessageBoxShow(Session["playsound"].ToString());
Session["playsound"] = 1;
}
but alway null?
---------------------------
Message from webpage
---------------------------
Object reference not set to an instance of an object.
---------------------------
OK
---------------------------
Sounds like your session might have timed out. If, between AJAX calls, your session expires on the server, then the ToString invocation may be operating on a null reference:
MessageBoxShow(Session["playsound"].ToString());
This would appear to coincide with what the AJAX client script is attempting to tell you.
This could also be the result of Session["playsound"]; being uninitialised.
The default session expiry duration for ASP.NET is 20 minutes, which you should be mindful of if you're executing an hour long timer.
You can use the
window.setInterval
method
It calls a function repeatedly, with a fixed time delay between each call to that function.
intervalID = window.setInterval(func, delay[, param1, param2, ...]);
Read more info
window.setInterval
On the client?
The only way I know to do this is via a javascript timer.
One way of doing this could be to have an session variable with NextTime to show the item on the page. If its null one could display the item now (or get the NextTime scheduled). On every page refresh, if the current time is after the Next Time, show the item and reset the NextTime session variable to the next Hour.
This would only work if the user is navigating the site and the page is being refreshed.
You can use the javascript variable window.name which keeps its value between page refreshes.
You could store a 'last checked time' in there and compare it with the current time.
If the user navigates to another site and that site clears this variable then your back to square one.
An easy answer would be to use a small cookie to store the original time and then query it every so often (~5 min?) this way the session won't run out and you're not SOL if the user leaves the page (if that's what you want).
DISCLAIMER: I haven't really dipped my toes into AJAX yet even though I've been programming ASP.net all summer, so excuse me if this isn't possible.

Caching strategy for asp.net

I would like to ask is there any way to achieve this functionality:
I have an Ajax enabled user web site (tree view on the left side, and content on the right side). When users selects a node on the left side, I need to store the last selected node in database. However the user can change the node quite often (even 5 or 10 times a minute).
So I would like to add it to the application cache, like:
cache["userName"]=selectedNodeID;
Is there any caching framework that would: perform certain action on a cache item if it wasn't changed for let's say 5 minutes? It would allow me to store last selected node id after 5 minutes from last change. If the user changes the node before this time passes - I would just update cache value and reset timer.
Any hints?
You could use the callback mechanism for when the cache item is removed from the cache. If you set the cache duration to 5 minutes, then you could check in your callback method whether the item has already been saved - if it hasn't then you can push it to the database. In either case, you can put the item straight back in the cache for another 5 minutes.
e.g. you would have something like this as the last parameter of your Cache.Insert method:
new CacheItemRemovedCallback (SaveItemToDatabaseAndReturnItToTheCache)
and then you would implement the callback method accordingly.
more info here: http://weblogs.asp.net/kwarren/archive/2004/05/20/136129.aspx

Resources