Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Here's an API concept which could be useful for performance optimisation. It's an example of key-based cache expiry applied to a broader internet-wide context instead of the internal Memcached-style scenario it seems to be mostly used for.

I want to make API calls almost as cacheable as static images. Using a news/feed subscriber as an example, which we might poll hourly, the idea is to send a last-updated timestamp along with each topic (it could just as easily be a version number or checksum):

{
  username: "Wendy",
  topics: [{
    name: "tv",
    updated: 1357647954355
  },
  {
    name: "movies",
    updated: 1357648018817
  },
  {
    name: "music",
    updated: 1357648028264
  }]
}

To be clear, this resource itself comes directly from the server every time and is not cached on the edge or by the client. It's our subsequent calls for topics that we can aggressively cache, thanks to the timestamp.

Assuming we want to sync all topics, we'd have "N" further calls to make in a naieve implementation (/topics/tv etc). But because of the timestamp, we can construct a URL like /topics/tv/1357647954355.json. The client usually doesn't make a call at all if it's already seen (and cached) the same version of that resource. Furthermore, even if it's new to the client, an edge cache (e.g. a reverse-proxy like Squid, Varnish, or service like Cloudflare) probably has seen it before, because some other user has probably opened the latest version of this topic already. So we still bypass the application server; the server only ever creates topic JSON once after the underlying resource has updated. So instead of N+1 calls to the server, the client probably makes a much smaller number of calls, and those calls will rarely hit the app server anyway.

Now for my question All this seems feasible and worth doing, but my question is if there's any prior art for this kind of thing and in particular, any HTTP standards to support it. I initially thought of conditional caching (ETags and modified dates), and I think they'd help to optimise this setup further, but I don't believe they are the answer. They are subtly different, because they require calls to be passed through to the application server in order to check something's changed. The idea here is the client saying "I already know the latest version, please send that resource back to me". I don't think there's any HTTP standard for it, which is why I propose a URL scheme like /topics/tv/1357647954355.json instead of some ETag-like header. I believe some CDNs work this way and it's surprising to me if there's no real HTTP standard around it.

Update: On reflection, an important special case of this is what a web browser does when it fetches a new HTML page. We know it will immediately be requesting CSS+JS, so the same versioning/timestamp trick can be used to ensure those static resources are cache-friendly. That this trick has not been formalised by the spec gives me confidence that unfortunately there is no HTTP standard for it. http://www.particletree.com/notebook/automatically-version-your-css-and-javascript-files/

share|improve this question
    
How does the client know that they have the most up to date timestamp for changes to the dataset? What if a change was made a few minutes after the client got the resource with timestamps and they have not polled again yet? Would you want them to get the cached data or the true latest version? –  Mike Jan 8 '13 at 15:38
    
For now, I'm mainly thinking about the N+1 polling scenario, ie where the child resources are immediately sync'd after the parent resource, so there's no risk of stale resources being fetched. –  mahemoff Jan 8 '13 at 15:59
    
There's a slight risk of a race condition where a topic is updated just after the user JSON is sent, but it shouldn't be a problem to most applications if the previous version of the topic is retrieved as it would have been used a minute earlier anyway. –  mahemoff Jan 8 '13 at 16:02
3  
The client will make the HTTP GET call unless it was previously told the cached item would still be valid for a period of time. That's your prior art, along with the relatively common use of /whatever.js?ver=12435 when the ver= is ignored but treated as a cache-invalidator by the client that doesn't know it's ignored. –  Ross Patterson Jan 9 '13 at 0:26
add comment

2 Answers 2

up vote 1 down vote accepted

As far as I can tell, you don't really need any specific HTTP support for what you want to do. At the core, you seem to want to be able to take a json fragment like

topics: [{
    name: "tv",
    updated: 1357647954355
},

and turn it into

GET /topics/tv/1357647954355.json

and have that returned from an edge cache or other store without hitting your server, presuming of course that a neighbour (to some Internet definition of local) has already requested the same page and therefore the local edge cache has it (assuming good cache-hints, etc)?

So long as your javascript processing your original json can perform the second AJAX-GET, then you should get pretty much what you are expecting to happen, that the number of requests hitting the central server for the individual tv/ URL are few, given their high cachability.

share|improve this answer
add comment

The solution is smart, but I would question if there is even any need to make this a non-cache request to fetch the JSON for dynamic processing using JavaScript at all.

If you were to just emit the latest version of the URLs directly in <script> and <link> elements as part of the HTML page that you serve up that would result in one less resource request. I guess it depends on exactly how large the HTML is that you're serving up in the first place, but even that could be served up with an ETag based on a hash of the latest dependent resource timestamps and, assuming they haven't changed, that would result in a 304 not modified for the HTML itself.

This way you're avoiding any JavaScript magic at all (which has to add bloat for the download logic too) and reducing requests by at least one.

share|improve this answer
    
I may be misunderstanding, but the HTML comment at the end was more about an analogy to the main idea, which is about a JSON API. The idea is to keep the personal, non-cacheable, part minimal, and the public cacheable stuff can sit on the edge. All this data changes quite often, so if it was all fetched in a single big response, that response would be new every time. –  mahemoff Jan 19 '13 at 4:55
1  
Yeah, I think I'm just not completely grasping your entire API interaction, but I do think the solution is wise. End game, if I'm at least grasping your main goal, you're reducing N GET -> 304 Not Modified requests down to a single GET -> 200 with this approach. –  Drew Marsh Jan 20 '13 at 23:23
    
Close. The possible alternatives I'm avoiding are (a) an expensive monolithic 200 and (b) a small 200 (like the one here) followed by N more 200s or relatively expensive 304s (because the comparison can't be edge-cached). –  mahemoff Jan 21 '13 at 4:41
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.