GoogleChrome / workbox

📦 Workbox: JavaScript libraries for Progressive Web Apps
https://developers.google.com/web/tools/workbox/
MIT License
12.39k stars 821 forks source link

CacheFirst image maxEntries not working #2768

Closed vell174 closed 3 years ago

vell174 commented 3 years ago

Library Affected: workbox v6.1.0

Browser & Platform: Google Chrome 89.0.4389.72".

Issue or Feature Request Description: Setting workbox:

registerRoute(
  new RegExp('.*/static/image/.*[.](jpg|png|svg|jpeg|PNG|SVG)$'),
  new CacheFirst({
    cacheName: 'api-image-casino-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [0, 200]
      }),
      new ExpirationPlugin({
        maxEntries: 400,
        maxAgeSeconds: 2592000,
        purgeOnQuotaError: true
      })
    ]
  }),
  'GET'
);

I want to see the limit of 400 images retrieved from the CDN.

What am I doing wrong?

image

jeffposnick commented 3 years ago

I'm not sure what the issue is—that does look like a valid configuration, and you shouldn't end up with more than 400 cache entries.

Is there anything logged in the JS console when your web app accesses images? In general, if you're using the development builds of Workbox, you should see quite a bit of detail logged, including messages related to cache expiration.

Is there a public URL at which your web app is deployed that I could access and try to reproduce the issue?

vell174 commented 3 years ago

@jeffposnick Hi, in console.log image image image

It is written that deleted tried to reload the page to go out and go back does not work

Doesn't work with image only

jeffposnick commented 3 years ago

Thanks for including that extra logging information in the screenshot. That confirms that workbox-expiration thinks that it's found and deleted the extra entries, at least.

I haven't seen this behavior before, and while I could try to reproduce it locally, is there any chance that you could either share your project's source code (if it's something I could run myself, which maybe isn't the case since there's a CDN involved?) or alternatively, could you deploy it to a publicly accessible URL that I could visit to reproduce the issue? If you can't share the URL on GitHub, feel free to DM @jeffposnick on Twitter.

jeffposnick commented 3 years ago

Thanks for the reproduction!

This issue is due to the presence of the Vary: Origin, Accept-Encoding header on the image responses. When Workbox calls cache.delete() to delete the entry, the Cache Storage API can't find the correct entry to remove.

If you use

new ExpirationPlugin({
  maxEntries: 400,
  maxAgeSeconds: 2592000,
  purgeOnQuotaError: true,
  matchOptions: {
    ignoreVary: true,
  },
})

it should work—that will cause the {ignoreVary: true} parameter to be passed through to the cache.delete() call.

jeffposnick commented 3 years ago

And for what it's worth, I understand that this can be very hard to debug. I've filed https://bugs.chromium.org/p/chromium/issues/detail?id=1186049 with the Chrome DevTools team (that issue might be flagged as private now, but it should be public in the future) to request that the DevTools Cache Storage viewer interface show the value of the Vary: header by default, and potentially link to this page, so that there's at least some clue as to what might be going on.

See also https://github.com/GoogleChrome/workbox/issues/2206, as this is a long-standing source of confusion.

alexandernst commented 1 year ago

I'm experiencing the same issue. This is my code:

import { CacheFirst } from "workbox-strategies";
import { ExpirationPlugin } from "workbox-expiration";
import { registerRoute, Route } from "workbox-routing";

self.addEventListener("install", () => {
  self.skipWaiting();
});

const assetRoute = new Route(({ request }) => {
  return request.destination === "script";
}, new CacheFirst({
  cacheName: "foobar",
  matchOptions: {
    ignoreVary: true,
  },
  plugins: [
    new ExpirationPlugin({
      maxEntries: 10,
      purgeOnQuotaError: true,
      matchOptions: {
        ignoreVary: true,
      },
    })
  ],
}));
registerRoute(assetRoute);

I noticed that I had 1582 scripts in the foobar cache storage and one new script was added every time I pushed an update to my website. I decided to add the ExpirationPlugin with maxEntries set to 10, which I thought would delete all scripts except 10, but it didn't. The plugin does avoid the total number of script to grow because every time a new script is loaded, the oldest one is deleted, but I expected the plugin to actually force-delete all but the 10 newest scripts.

Why does this happen?