JavaScript Caching – Adopting Perfect Caching for JS Apps

The Problem

JavaScript Single-Page-Applications tend to consist of a lot of JavaScript files. Several hundreds of JS files are no exception.

When accessing these applications using a web browser, every single JavaScript file has to be fetched using a distinct request. Unfortunately, browsers support only a limited number of parallel connections/requests. Older versions of Internet Explorer were limited to only two concurrent connections. Most modern browsers support six to eight concurrent connections, but this is still not enough to effectively fetch several hundreds of files.

Additionally, JS frontends these days can be several megabytes in size which need to be fetched from the server.
This results in wait times and high server load.

To reduce the wait time and server load, one could cache these files in the browser.

But how long should these files be cached?
And when they are cached and you deploy a new release of your application, users would have to delete their browser cache manually, which is not very practical.

Introducing Perfect Caching

The engineers of Google came up with a simple but effective solution to this problem – an approach called “Perfect Caching”.
It is implemented in Google’s web framework GWT. But even if you are not using GWT, the approach can be easily adopted for any JavaScript application.

This is how it works:

  1. Combine all JavaScript files into one big JavaScript file.
  2. Name the combined JS file using the pattern <hashcode>.cache.js
    • calculate the hash of the contents of this file
    • use a common hashing algorithm like MD5 or SHA1
  3. Instruct the browser to cache files matching the pattern *.cache.*
    as long as possible.

    • You can do this via Apache by adding an entry to the .htaccess file.
    • If your app is a Java application you can alternatively use a simple servlet filter.
  4. Instruct the browser to never cache files matching the pattern *.nocache.*.
    • call the resource that includes your JavaScript file (your host page) accordingly, e.g. index.nocache.html

Whenever a JavaScript source file is changed, the file name of the combined JavaScript file will change, so the browser is forced to fetch the new file. It is essential to prevent caching of the host page, as this page contains the
URL to the combined JavaScript file.

The result of this approach is as follows:

  • When you access the application for the first time, all your JavaScript code is fetched once with a single request.
  • Subsequent access to the application leads to no additional requests. As the JavaScript is cached by the browser, the browser doesn’t even
    have to ask the server if the file has been changed since last access.
  • When a new release is deployed, the browser will fetch the new JavaScript code. Once. Again, subsequent access doesn’t lead to requests to
    the server.

Integration into Java Apps Using Servlet Filter

As said before, one possibility to instruct the browser to cache files matching the pattern *.cache.* and never cache file matching *.nocache.* is to use a servlet filter. E.g., such a servlet filter could look like this:

public class ResourceCachingServletFilter implements Filter {
  public void destroy() {}

  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest httpReq = (HttpServletRequest) req;
    HttpServletResponse httpResp = (HttpServletResponse) resp;

    if (httpReq.getRequestURI().contains(".nocache.")) {
      httpResp.setHeader("Cache-Control", "no-cache");
    } else if (httpReq.getRequestURI().contains(".cache.")) {
      httpResp.setHeader("Cache-Control", "max-age=2592000");
    }
    chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {}
}

Integration into a Gradle Build

If you are using Gradle as your build tool, you can use the following two plugins in combination to implement perfect caching:

  • Gradle JavaScript Plugin – this plugins allows to combine JavaScript files into a single file, either by simply concatenating the files or by using the RequireJS Optimizer. If you are using RequireJS you just have to point the plugin to your root module and it will traverse the dependency tree and concatenate all the files in the correct order.
  • Resource-Caching Gradle Plugin – This plugin will generate the filename of the combined JavaScript file using the files hash. Additionally, it can inject the required script tag into your host page.

Here you can see a sample build.gradle file which uses these two plugins in combination to build a Java WAR file that uses Perfect Caching.

Short URL for this post: http://wp.me/p4nxik-2fL
This entry was posted in Web as a Platform and tagged , , , , . Bookmark the permalink.

One Response to JavaScript Caching – Adopting Perfect Caching for JS Apps

  1. Pingback: TCP and HTTP for web developers

Leave a Reply