I want to know how to mitigate issues that arise when a user is interacting with your site via AJAX, and you deploy a new version of the site at the same time.
Imagine a scenario like the following:
A user is navigating through your posts by clicking a 'Next' button. The 'Next' button submits a form via XHR to the server, which then responds with a Server-generated Javascript Response (SJR).
The SJR renders this Javascript:
// in create.js.erb renderNewPost(<%= @post.to_json %>);
(renderNewPost is defined in your concatenated, minified application.js and is cached by the user's browser.)
Everything is working fine at this point.
Now, as part of a new feature, you change the SJR for the endpoint to first log something:
// in create.js.erb logSomething("hi!"); renderNewPost(<%= @post.to_json %>);
and add the function logSomething to your application.js.
The user clicks the 'Next' button again. This time, the SJR responds with the logSomething('hi') line... but the browser doesn't have logSomething defined anywhere, because the application.js the browser has loaded is the first version.
Now the user won't be able to use your site until the page is refreshed.
How can we solve this problem? Is there a name for the issue at hand?
1 Answers
Answers 1
Option 1:
One easy option would be, to have the responses be as self-contained as possible, meaning that in your example, you would not have the log_something method defined somewhere else, but also in the same response.
Option 2:
Another solution, which might be more general could be, that you send some kind of version in a custom response header (lets say X-SJR-Version) in the application controller
before_action do response.headers['X-SJR-Version'] = '1.0.0' end and add some Javascript client code, where you perform your AJAX request that checks if the versions still match. A rough implementation could be something like this (while untested):
var version = null; $(document).bind("ajaxSuccess", function(xhr, status){ var newVersion = xhr.getResponseHeader('X-SJR-Version') if(version === null) { version = newVersion } // initial version if(newVersion !== version) { location.reload(); } }); This makes use of the jquery hook ajaxSuccess, to perform the version check. You might want to check this page if you are using regular jquery-ujs rails calls.
Modern day approach:
However, modern day rails applications usually do not use server generated javascript that often anymore, since the architecture there is often to write the client javascript yourself (often with react, angular or vue) and consume JSON API endpoints for data from the server, while the javascript code is already present in the response of the page and no additional JS needs to be loaded.
This has the advantage, that you can use API-Versioning, while the loaded javascript will stick to the old version, so you will have a smooth shift to newer API versions (while usually you often would not even need new API versions, since only the javascript changes)
Therefore you will most likely not find an out of the box solution to this problem and have to go for something like the above.
0 comments:
Post a Comment