Skip to content

Commit cb40dd3

Browse files
committed
Merge pull request twitter#1183 from jharding/1110-max-size-reached
Handle local storage quota exceeded exception
2 parents 97eb7af + 03e8eaf commit cb40dd3

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

src/bloodhound/persistent_storage.js

+36-17
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,7 @@ var PersistentStorage = (function() {
3232
this.ls = override || LOCAL_STORAGE;
3333

3434
// if local storage isn't available, everything becomes a noop
35-
if (!this.ls || !window.JSON) {
36-
this.get =
37-
this.set =
38-
this.remove =
39-
this.clear =
40-
this.isExpired = _.noop;
41-
}
35+
!this.ls && this._noop();
4236
}
4337

4438
// instance methods
@@ -55,6 +49,26 @@ var PersistentStorage = (function() {
5549
return this._prefix(key) + this.ttlKey;
5650
},
5751

52+
_noop: function() {
53+
this.get =
54+
this.set =
55+
this.remove =
56+
this.clear =
57+
this.isExpired = _.noop;
58+
},
59+
60+
_safeSet: function(key, val) {
61+
try {
62+
this.ls.setItem(key, val);
63+
} catch (err) {
64+
// hit the localstorage limit so clean up and better luck next time
65+
if (err.name === 'QuotaExceededError') {
66+
this.clear();
67+
this._noop();
68+
}
69+
}
70+
},
71+
5872
// ### public
5973

6074
get: function(key) {
@@ -67,14 +81,14 @@ var PersistentStorage = (function() {
6781

6882
set: function(key, val, ttl) {
6983
if (_.isNumber(ttl)) {
70-
this.ls.setItem(this._ttlKey(key), encode(now() + ttl));
84+
this._safeSet(this._ttlKey(key), encode(now() + ttl));
7185
}
7286

7387
else {
7488
this.ls.removeItem(this._ttlKey(key));
7589
}
7690

77-
return this.ls.setItem(this._prefix(key), encode(val));
91+
return this._safeSet(this._prefix(key), encode(val));
7892
},
7993

8094
remove: function(key) {
@@ -85,14 +99,7 @@ var PersistentStorage = (function() {
8599
},
86100

87101
clear: function() {
88-
var i, key, keys = [], len = this.ls.length;
89-
90-
for (i = 0; i < len; i++) {
91-
if ((key = this.ls.key(i)).match(this.keyMatcher)) {
92-
// gather keys to remove after loop exits
93-
keys.push(key.replace(this.keyMatcher, ''));
94-
}
95-
}
102+
var i, keys = gatherMatchingKeys(this.keyMatcher);
96103

97104
for (i = keys.length; i--;) {
98105
this.remove(keys[i]);
@@ -125,4 +132,16 @@ var PersistentStorage = (function() {
125132
function decode(val) {
126133
return $.parseJSON(val);
127134
}
135+
136+
function gatherMatchingKeys(keyMatcher) {
137+
var i, key, keys = [], len = LOCAL_STORAGE.length;
138+
139+
for (i = 0; i < len; i++) {
140+
if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
141+
keys.push(key.replace(keyMatcher, ''));
142+
}
143+
}
144+
145+
return keys;
146+
}
128147
})();

test/bloodhound/persistent_storage_spec.js

+41
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,47 @@ describe('PersistentStorage', function() {
8181
expect(ls.setItem.argsForCall[0])
8282
.toEqual(['__ns__key__ttl__', ttl.toString()]);
8383
});
84+
85+
it('should call clear if the localStorage limit has been reached', function() {
86+
var spy;
87+
88+
ls.setItem.andCallFake(function() {
89+
var err = new Error();
90+
err.name = 'QuotaExceededError';
91+
92+
throw err;
93+
});
94+
95+
engine.clear = spy = jasmine.createSpy();
96+
engine.set('key', 'value', 1);
97+
98+
expect(spy).toHaveBeenCalled();
99+
});
100+
101+
it('should noop if the localStorage limit has been reached', function() {
102+
var get, set, remove, clear, isExpired;
103+
104+
ls.setItem.andCallFake(function() {
105+
var err = new Error();
106+
err.name = 'QuotaExceededError';
107+
108+
throw err;
109+
});
110+
111+
get = engine.get;
112+
set = engine.set;
113+
remove = engine.remove;
114+
clear = engine.clear;
115+
isExpired = engine.isExpired;
116+
117+
engine.set('key', 'value', 1);
118+
119+
expect(engine.get).not.toBe(get);
120+
expect(engine.set).not.toBe(set);
121+
expect(engine.remove).not.toBe(remove);
122+
expect(engine.clear).not.toBe(clear);
123+
expect(engine.isExpired).not.toBe(isExpired);
124+
});
84125
});
85126

86127
describe('#remove', function() {

0 commit comments

Comments
 (0)