angular-cache icon indicating copy to clipboard operation
angular-cache copied to clipboard

maxage not working with $http and localstorage with passive deleteOnExpire setting

Open ekallevig opened this issue 9 years ago • 10 comments

I'm using angular-cache with $http and localStorage. I use a maxAge and "passive" setting for deleteOnExpire. The caching works fine, but after exceeding the maxAge time (expired), it continues to fetch from cache. If I change deleteOnExpire to 'aggressive', it works as expected (fetches from server after maxAge has elapsed). When I switch it back to memory storageMode (with passive), it works as expected (after item expires, it fetches from server).

My reading of the docs was that passive simply means it doesn't clear the expired value until the cache is requested again (vs aggressive, where it clears it the second the maxAge has elapsed).

Am I doing something wrong or is this expected?

ekallevig avatar Apr 19 '16 17:04 ekallevig

+1. I have seen the same results, where when you use localStorage (or sessionStorage) with $http and passive deleteOnExpire and maxAge, the cache items appear to never expire.

rsc092020 avatar Jun 17 '16 21:06 rsc092020

Same here, also with sessionStorage as mentioned above.

magicznyleszek avatar Aug 24 '16 09:08 magicznyleszek

So the settings to reproduce are:

{
  storageMode: 'localStorage',
  deleteOnExpire: 'passive'
}

plus using $http, correct?

jmdobry avatar Aug 25 '16 14:08 jmdobry

Yes, that's correct, plus a maxAge. It will just stay around in localStorage without getting fetched again from server, even after the expiration according to maxAge.

rsc092020 avatar Aug 31 '16 15:08 rsc092020

I think this is related. I'm using (was using) this config with the $http service:

{
    maxAge: 4 * 60 * 60 * 1000,
    deleteOnExpire: "passive",
    storageMode: "localStorage",
}

Although I could see keys being added into local storage, every time I refreshed the page the cache would be ignored. Changing "passive" to "aggressive" is an okay workaround for me. If I can spend any time looking at this later I will.

robations avatar Nov 24 '16 16:11 robations

A plunker to reproduce would be great, if you can

jmdobry avatar Dec 12 '16 03:12 jmdobry

Probably need to open this in a separate window otherwise you'll be hit by iframe restrictions on localstorage. Hit the update button and it will make a request that should be cached for 5 minutes. (Open the console to see network and log feedback.)

As far as I can see, this example is working correctly, so something else must be triggering the issue.

https://plnkr.co/edit/kApzDm20xH7rA2eeKxku?p=preview

Need to move on but will investigate later.

Edit: I was looking out for the problem I commented on, not necessarily the OP’s issue...

robations avatar Dec 12 '16 12:12 robations

The plunkr by @robations is an example of the original issue that was brought up. After fetching the first time, it will just consistently use the cache after that even if it's older than the max-age (Try lowering the max-age to make this easier to see). I haven't had enough time to look at this close enough to come up with a fix, but basically what is happening is it will hit this code in the get function when trying to retrieve a cached http request that is in localStorage (or anything that isn't in-memory):

if ($$storage) {
  if ($$promises[key]) {
    return $$promises[key];
  }

  var itemJson = $$storage().getItem(this.$$prefix + '.data.' + key);

  if (itemJson) {
    item = utils.fromJson(itemJson);
  } else {
    return;
  }
} else if (utils.isObject($$data)) {
  if (!(key in $$data)) {
    return;
  }

  item = $$data[key];
}

then down below in the same method is this piece of code that will remove the item if it's expired:

if (this.$$deleteOnExpire === 'passive' && 'expires' in item && item.expires < now) {
    this.remove(key);

But it will return before it reaches this code in certain conditions (see return $$promises[key];). This will happen every time when using a storageMode that is not memory and when the item being cached is a promise (not necessarily just from $http). I don't believe this is expected behavior.

rsc092020 avatar Jan 13 '17 22:01 rsc092020

I think

 const getHandler = (shouldStore, isError) => {
      return (v) => {
        if (shouldStore) {
          this.$$promises[key] = undefined

should be rewritten with

 const getHandler = (shouldStore, isError) => {
      return (v) => {
        this.$$promises[key] = undefined
        if (shouldStore) {

so in next cache.get it reach cleanup part

Delagen avatar Feb 28 '17 05:02 Delagen

@jmdobry I'm having a similar problem - looks as if you have fixed this on the CacheFactory repo - however not updated the dependency on this repository?

clarkieryan avatar Jun 19 '17 09:06 clarkieryan