jmdobry / angular-cache

angular-cache is a very useful replacement for the Angular 1 $cacheFactory.
http://jmdobry.github.io/angular-cache
MIT License
1.39k stars 156 forks source link

How managing "Exceeded the quota" #136

Closed bennekrouf closed 9 years ago

bennekrouf commented 9 years ago

I am using the cache for a big app, and I frequently get this error :

Error: Failed to execute 'setItem' on 'Storage': Setting the value of 'angular-cache.caches.dataCache.data.../webapp/rest/auth/dataobjects/conferenceassetprofiles/....... exceeded the quota.

I tried to change the capacity. But still getting the error. How can I manage this problem ?

Also, "capacity" is it in Mo ? Ko ? number of entries ?

Thanks for your help

jmdobry commented 9 years ago

The capacity of the cache refers to the maximum number of items that can be in the cache. If you exceed the capacity of the cache it will simply remove the least recently used item(s).

Exceeded the quota means you've filled up localStorage, which can generally store only about 5 MB of data. Angular-cache does not know how big your items are, just how many of them are in the cache. Either don't store so much data, or find a more aggressive way to bust the cache. Try StackOverflow for how to manage this problem.

bennekrouf commented 9 years ago

What about checking the size before adding an item (in angular-cache) ?

Personal point of view : "capacity" based on a "number" of items is useless.

jmdobry commented 9 years ago

What about checking the size before adding an item (in angular-cache)?

There is absolutely no way for angular-cache to calculate how much data has been stored in localStorage. Anything from any other code could also be putting stuff into localStorage, and localStorage provides no mechanism for retrieving the keys or values of all stored items.

Personal point of view : "capacity" based on a "number" of items is useless.

That's how Angular's $cacheFactory works, so that's what angular-cache must do to maintain compatibility. Several times I have had need to store a limited history of items, and this feature has served quite well for that use case.

bennekrouf commented 9 years ago

It is not the best way to do, but It can be managed with exceptions.

try { this.$$storage.setItem(this.$$prefix + '.data.' + key, angular.toJson(item)); } catch(e){ this.remove(this.$$lruHeap.peek().key); this.$$storage.setItem(this.$$prefix + '.data.' + key, angular.toJson(item)); }

This code works if the objects are of the same size. Otherwise, we can calculate the size of the new item and looping on removing all the old items until enough space is available.

jmdobry commented 9 years ago

Maybe you could illustrate with a PR?

bennekrouf commented 9 years ago

I am trying to ... It is almost working, but I need some help.

I want to get the oldest item and remove it. But in some cases, I have an error saying that the item I am trying to remove does not exists. May be I am not doing the right thing :

To get the oldest I do :

this.$$storage.getItem(this.$$prefix + '.data.' + this.$$lruHeap.peek())

// I tried this.$$storage.getItem(this.$$prefix + '.data.' + this.$$lruHeap.peek().key)

and for removing I do :

this.remove(this.$$prefix + '.data.' + this.$$lruHeap.pop().key);

// I tried : this.remove(this.$$prefix + '.data.' + this.$$lruHeap.peek().key);

This code is inside this :

var targetItemSize = angular.toJson(item).length; var releasedMemorySize = 0, itemToFlush;

  while(releasedMemorySize < (targetItemSize * 2) ){ 
    // we release 2 * memory more than the size of the target item to avoid generating too much exceptions
    if(this.$$storage){....

Thanks !!

jmdobry commented 9 years ago

Just submit a PR which I can review.