-
-
Notifications
You must be signed in to change notification settings - Fork 434
Deploying a site breaks it for users who have the site open in their browsers #389
Comments
Oh man, that's a really good point that I hadn't considered. This is definitely a high priority to fix. Thanks |
This problem is not unique to Sapper; I've experienced it when deploying React and Angular apps also. To avoid it, something like this works:
|
This is more of a caching issue, isn't it? When you deploy any site with versioned assets, one would expect that a 30+ day lifespan is fine. Technically, they are immutable, and should even be cached as such. (Most of my projects have a 1Y Cache-Control for versioned assets. So, I think the real problem is not leveraging a CDN, or using one but purging it between deployments. ZEIT started doing this "as a feature" which is a big mistake IMO. Proper |
Caching won't fix this. A user might load the index page on day 1, then navigate to a new route (and need new assets they never loaded before, thus never cached) on day 5. (But yes - it is good to mark all the hash-named files as cache-forever-immutable!) |
The file(s) wouldn't exist on the user disk, but they should exist on the CDN. The |
While using CDNs is great, sapper should probably still attempt a full refresh in case dynamic imports fail with 404s (on navigation only, or else we could potentially get infinite loops in some error conditions). |
Perhaps on each request, sapper sets a cookie with the current site version? Client triggers a reload if the version changes, no one browses an old site. |
Please we can update the documentation for export to highlight this issue and advise on best practice for mitigating the issue. I'm happy to work on this. What is the best practice? Ensure that hashed files remain available after each deploy, anything else which needs to be done? What is a good caching strategy? |
It's not just a problem with export. I've been having the same problem with the server deployment. Solved it for now (I hope) by forking Sapper and setting cache headers for page routes (https://github.com/sveltejs/sapper/blob/master/templates/src/server/middleware/get_page_handler.ts#L45) to See also #415. |
I think I've just run into exactly the same issue. I was wondering if the service worker could be a possible cause? My site is simply run using build, not export - but an interesting factor is that the link which broke was one which pointed to an anchor on the page it linked to: |
This was the initial response 1 year ago, and I still think this is a serious problem. I just wanted to bring this issue to @sw-yx 's attention, as he seems to be the currently active maintainer (?). |
not a maintainer. just an interested party trying to get up to speed. i agree this is an important issue but also it seems we can tweak the rollup/webpack bundle ourselves to name chunks? (i havent actually thought this through, just offering userland suggestions). ditto adding headers on routes. |
This is due to the old assets not being available in a cached/ currently loaded file, right? In which case, since we use dynamic imports for chunks and such, can't we catch any loading errors on import and force a full reload when they occur? |
@pngwn that would be my proposed solution, but I was thinking maybe the sorcerors here have better ideas that don’t require a full reload. But I’d gladly accept a reload instead of crashing! |
Funnily enough, only this morning I set up the reload solution I mentioned earlier in the thread (adding the cookie with Cloudflare workers instead of Sapper, since EVERY request, even CDN-cached requests, must possess the cookie): https://gist.github.com/njbotkin/9a170999e23fb34d4113634a6aba47b0 It's not for everyone, and certainly doesn't resolve this issue, but this is the nicest way I can think of to arbitrarily trigger SPA reloads. |
@arggh The only thing that comes to mind is loading the manifest in, so we could grab up to date information from a fresh manifest if a request failed but then what if we just end up getting a cached manifest anyway (since the path to the manifest would need to be constant for this to work)? What if there are substantial changes that render such a manifest completely redundant? |
@pngwn you are right, it's probably not really feasible. HMR in production seems like begging for trouble. Honestly I haven't given much thought to this, as I'd personally just go with the full reload -solution. To make it nice, Sapper could provide a way to intercept the reload event, giving a chance to provide a nice UI that explains the situation to the user and lets them manually click "Reload site". |
As a sidenote, Meteor's solution for dynamic imports work pretty much like that: each module is fetched, cached forever until it actually changes, and will not be downloaded again. Updating a single line of code and releasing a new version of the app does not invalidate all modules. Only the changed modules will be re-fetched. They call it "exact code splitting". |
I think I have an issue where Firefox Nightly for Android is "hard remembering" my webpack sapper app hosted on Netlify (which claims to have Instant Cache Invalidation). Refreshes still lead to Most of the suggestions here are not possible on the mobile version of Firefox, the ones that were possible did not fix the issue: https://stackoverflow.com/questions/41636754/how-to-clear-a-service-worker-cache-in-firefox I don't know which of 1 or more is causing the issue: FFN, Netlify, Service Workers, Sapper. and it's pretty hard to tell when you don't have the issue outside of the phone. [Edit] I figured out the cause of my issue. I installed Sentry and discovered that Firefox does not support named capture groups in regexes. Sapper should get an official Sentry plugin. |
Hi, I'm trying to find the best approach to deal with this problem. I'm not a fan of the idea of keeping the old files and deleting them in the future: the app did change and I can't simply wait for the user to hard refresh it in order to get the new version. And I'm not a fan of automatically hard refreshing the user browser while he's using it: what if he's writing something for hours and I destroy all his work? Plus, correct me if I'm wrong: if I deploy a new version of my app, the user won't experience any problem at all unless he navigates to another page. Right? He can totally continue doing what he's already doing and it'll work properly until he'll navigate to another page. So, what if I store the current version of my app on my database (I'm using firestore) and if that value changes while someone is using the app, I force a hard refresh BUT only when the user navigate? So, something like: Would this work? Or we can do that, but when we get this error "Failed to load resource: the server responded with a status of 404 (Not Found)". If this error happens we can fetch a JSON file appending a timestamp to query string. On that JSON we store the new version number and if it's different to the current one we hard refresh. |
@wavesforthemasses I solve this with a message on the |
@mikenikles in the end I did something similar to your solution using this on that page:
|
Using the above suggestion worked for me but added an uncomfortable flash before the reload happened. I'm experimenting with putting this reload logic directly within the |
Hi, I'm also encountering this issue with a static export of a sapper app on both Vercel and Netlify. Is there any workaround? It seems to me that if the ressources of the previous deploy aren't available anymore, the |
I wonder if we should put a sort of token file at a well-known path that is updated to contain a new value on deploy. If we see a failure to load a chunk or other file, we could check the contents of this file and issue a full navigation or page reload if it's changed. |
We also struggled with this issue. We have found a solution that works for us, though it was a fast patch rather than a thought-through solution. The algo is the following: inside the |
For future readers, I have been able to solve this issue by doing the following : Publish a version numberI create a file called {
"scripts": {
"build": "sapper export && date '+%s%N' > __sapper__/export/_version"
} We now have the version number available at Update the service workerWe need to update the service worker to disable the cache for this resource. It would be more elegant to rely on an appropriate HTTP header but for now I have hard-coded the bypass logic: // service-worker.js
self.addEventListener("fetch", (event) => {
const url = new URL(event.request.url);
if (url.pathname === "/_version") {
return;
}
// ... Detect when a new version is availableIn our main layout file, or any other entry file that is included in the application, we create a // routes/_layout.svelte
<script>
import { onMount } from "svelte";
let clientVersion;
let newVersionAvailable = false;
onMount(() => {
const watcher = window.setInterval(async () => {
if (process.env.NODE_ENV === "development") {
return;
}
const res = await fetch("/_version");
const serverVersion = await res.text();
if (serverVersion && !clientVersion) {
clientVersion = serverVersion;
} else if (res.ok && serverVersion != clientVersion) {
newVersionAvailable = true;
window.clearInterval(watcher);
}
}, 10 * 1000);
});
</script> Reload the app transparentlyWe register a global click event to do a full reload of the application when the user click on a link : <!-- routes/_layout.svelte -->
<svelte:window
on:click|capture={(event) => {
if (
newVersionAvailable &&
event.target &&
event.target.href &&
event.target.target !== "_blank"
) {
event.preventDefault();
event.stopPropagation();
window.open(event.target.href, "_parent");
}
}}
/> I haven't have the opportunity to test this logic extensively but so far it seems to works great. |
@mquandalle This looks amazing, I'll test it today and give a feedback. I used to run into this issue only while user were actively using my website during an update, but right now I'm facing a new issue on mobile (on a different project): after any update the website breaks unless I empty all the cache. So, I'll see if your solution will fix this. Since it happens to me every time I update the website, it'll be easy to see if it's fixed or not :) |
Closing as a duplicate of sveltejs/kit#87 |
I noticed that deploying a Sapper site changes the hashes of all the bundles, so that any navigation, dynamic importing etc. breaks for any currently active users of that site.
The experience for the user is that... basically the site just stops working. He/she clicks a link and nothing happens, except for the title changing to
500
and this being printed at the bottom of the page (where it's highly unlikely to get noticed):In the console, we get:
I get why this is happening, but in my opinion it could be handled a bit more gracefully. Imagine if your site has 10 000 active users simultaneously? I guess one could do rolling deployments so that the old instances are sticky until all the users are dropped off, but that's a tad difficult for most of us.
If it's of any help, GatsbyJS bumped into the same problem here: gatsbyjs/gatsby#4779
The text was updated successfully, but these errors were encountered: