HTML5 Sockets: In this case, a server can send a request to any client to say go and get the new stuff.
Comet style polling: The client consistently polls the server in the background asking if there is a new version available. When the server says there is, the client is triggered to start cache busting. Both of these are good approaches, but in some edge cases may not be available.
The architecture may not allow HTML5 sockets. For example, some secure banking website just doesn't like it.
The Comet style polling might be too intensive and may just leave open edge cases where a critical update is needed but the polling thread is waiting to be fired.
Client Refresh Notification Pattern
Recently on a project, neither of the above approaches was available, so I needed something else. The solution was based on some existing concepts already in the architecture (which are useful for other things) and some new ones that needed to be introduced:
- The UI always knows the current version. This was already burnt into the UI as part of the build process.
- Every UI was already sending up the version it is on in a custom header. It was doing this for every request. Note: this is very useful as it can make diagnosing problems easier. For someone seeing problems, it's nice to be able see that they're on an old version straight away.
The following new concepts were then introduced:
- The server would always store the latest client version it received. This is easy to do and, again, a handy thing to have anyway.
- The server would then always add a custom header to every response to indicate the latest client version it has received. Again, a useful thing to have for anyone doing a bit of debugging with Firebug or Chrome web tools
- The client would then have some simple logic so that when it saw a different version in response to the one it sent up, it knows there's a later version out there, and that's the trigger to start cache busting! This should be done in central place—for example, an Angular filter (if you are using Angular).
- Then, as part of every release, just hit the server with the latest client in the smoke testing.
- As soon as any other client makes a request, it will be told that there is a later client version out there, and it should start cache busting.
So, the clients effectively tell each other about the latest version, but without ever talking to each other. It's all via the server. It's like the mediator pattern. But it's still probably confusing. So let's take a look at a diagram.
With respect to diagram above:
- UI 1 has a later version (for example 1.5.1) of static assets than UI 2 (for example 1.5.0).
- Server thinks the latest version of static assets is 1.5.0.
- UI 1 then makes a request to the server and sends up version 1.5.1.
- Server sees 1.5.1 is newer than 1.5.0 and then updates its latest client version variable to 1.5.1.
- UI 2 makes a request to the server and sends up the version of static assets it has, which is 1.5.0.
- Server sends response to UI 2 with a response header saying that the latest client version is 1.5.1.
- UI 2 checks and sees that the response header client version is different from the version sent up and starts busting the cache
Note: In this example I am using two web clients and one back-end server. But note the exact same pattern could be used for back-end microservices that communicate with each other. Basically, this could be used for anything where there is a range of distributed clients (they don't have to be web browsers) and caching of static resources.
Until the next time, take care of yourselves.