vitedge icon indicating copy to clipboard operation
vitedge copied to clipboard

Unable to load some site assets.

Open TimTinkers opened this issue 3 years ago • 6 comments

The issue we are experiencing is one related to KV assets. For context: our site is a Vue 3 application. We are building it using Vitedge to support SSR running on a Cloudflare Worker.

The issue as reported by one of our devs:

  1. We deployed our application using Vitedge. In doing so, we deployed a new worker and KV:  - Worker: test-assets   - KV: __test-assets-workers_sites_assets (c46d5f240d0643fe9f822ae3a25595de)

  2. We opened the page that the Wrangler GitHub action had spun up for our Worker: https://test-assets.superfarm.workers.dev/ in incognito mode and waited until test-assets redeployed, then navigated to one of our pages ("NFT Drops") and experienced some HTTP 500 errors that some of our assets could not be found.

  • https://test-assets.superfarm.workers.dev/assets/Banner.6a23aecd.css   - https://test-assets.superfarm.workers.dev/assets/Stats.af469f3b.js   - https://test-assets.superfarm.workers.dev/assets/index.b28d30af.js   - https://test-assets.superfarm.workers.dev/assets/ViewDrop.e0204051.js   - https://test-assets.superfarm.workers.dev/assets/Banner.e6440b01.js
Error: 1101
Ray ID: 6ccb5309f8d94991
Timestamp: 2022-01-13 02:51:43 UTC
  1. Checking our logs for the 500 error isn't helpful.```  "exceptions": [     {       "name": "Error",       "message": "internal error",       "timestamp": 1642042501244     }   ],

4. Further digging shows that the assets in the URL don't seem to match those that were stored in KV:```
$ wrangler kv:key get assets/Stats.af469f3b.js --namespace-id c46d5f240d0643fe9f822ae3a25595de
Code 10009: get: 'key not found'

The actual asset name:``` $ wrangler kv:key get assets/Stats.af469f3b.9ec2057bc5.js --namespace-id c46d5f240d0643fe9f822ae3a25595de


We're currently assuming that either Vitedge or Wrangler are adding some unique hash to the end of our assets. Vitedge is getting its assets using the `@cloudflare/kv-asset-handler` package so this might be a known issue in that handler?

We would love it if you could share some insights on this or help further debug.

TimTinkers avatar Jan 13 '22 07:01 TimTinkers

I'm back with a bit of a clarifying example. Here's the persistence issue as we understand it:

  • one of our users opens a page in their browser, let's say one being served by the worker to dev.superfarm.workers.dev.
  • we deploy a new build to dev.superfarm.workers.dev
  • the user will begin experiencing the issues above regarding inability to load assets if they attempt to load a page containing a component which has been updated in the new build; this issue will persist until the user reloads the page.

For example: we have a "Stats" component in the top banner of our page that gets loaded when the user lands on the app. If a user loads our app and then tries to navigate to our "NFT Drops" page after we change the "Stats" component in our newly-deployed build, that asset hash will change and the user will start experiencing the problems above.

I hope this helps find some solution.

TimTinkers avatar Jan 14 '22 08:01 TimTinkers

@TimTinkers That example is definitely useful! I don't think Vitedge is doing anything special with assets, it's just leveraging Vite and Wrangler. I didn't have time yet to check myself but here are some pointers:

  • Assets are normally hashed to avoid getting old files from CDNs. You can add logs in Vitedge around assets using willRequestAsset and didRequestAsset hooks (the last one can actually modify the response). Also, most of the code for assets is here, in case you want to check.

  • I've seen some websites that get a cached asset map in the browser and keep requesting the same old assets even if the server has already been deployed and busted the old assets from CDN (I think this happened in Netlify). As far as I know, Wrangler does not remove old assets from cache on new deploys but I might be wrong, or this might be the case for *.worker.dev domains.

  • Vite is naming built files as [name].[hash].[ext]. I see in your example you have assets/Stats.af469f3b.9ec2057bc5.js, with 2 hashes, but I don't know what is adding the second one. As far as I remember, Wrangler didn't add any 🤔 You can play with Vite build hashes by passing these options in the plugin as Rollup options:

vitedge({
      client: {
        build: {
          rollupOptions: {
            output: {
              entryFileNames: `assets/[name].js`,
              chunkFileNames: `assets/[name].js`,
              assetFileNames: `assets/[name].[ext]`,
            },
          },
        },
      },
    })

The previous options will build your assets without hashes at the Vite level.

I'll try to check when I have some more time but let me know if you find something.

frandiox avatar Jan 17 '22 11:01 frandiox

Hi @frandiox thanks for the added information, we're still continuing out own debugging. Just curious if you've had any additional time yet to look at this one.

TimTinkers avatar Feb 02 '22 05:02 TimTinkers

@TimTinkers hey! Not yet, quite busy here moving apartments and stuff. Still in my list though!

frandiox avatar Feb 02 '22 10:02 frandiox

@TimTinkers Hello! Did you manage to find a workaround on your end?

I don't think this issue can be fixed in Vitedge easily because it's basically how SPA work. Once the old assets are removed from CDN, live users who don't yet have the new app version will need to refresh their browser to get it.

What we did once in a Vue SPA app (on Netlify) was something like this:

router.onError((error) => {
  // When a new version of the app is deployed, old version's files are
  // removed from CDN. Browsers that have old files cached will try to
  // download more old files on route navigation.
  // This will hard refresh browsers that fail when loading files in
  // order to get the updated version of the app.
  if (/loading chunk chunk-(\d|[a-z])+ failed\./i.test(error.message)) {
    const key = 'chunk-failed-reload'
    const lastReload = localStorage.getItem(key) || 0
    // Do not reload more than once a minute to avoid infinite loops.
    if (lastReload < Date.now() - 60000) {
      localStorage.setItem(key, Date.now())
      window.location.reload()
    }
  }
})

This basically detects the error and refreshes the browser. In this case, it was something like loading chunk chunk-lkj213lk.js failed blah but you might need to adjust it. This could be used to save Vuex state in localStorage before refreshing or anything like that.

frandiox avatar Feb 23 '22 12:02 frandiox

Hi @frandiox thanks for the reply! No, we unfortunately haven't had time to continue working on this project and didn't end up resolving this one on our end. Thanks for the tip though regarding the CDN assets, that makes sense. We'll keep looking into it.

TimTinkers avatar Feb 24 '22 19:02 TimTinkers