Skip to content

Commit

Permalink
Merge pull request #654 from thgreasi/websqlDbQuotaRetry
Browse files Browse the repository at this point in the history
feat(websql): retry a failed setItem caused by Quota error
  • Loading branch information
thgreasi committed Feb 9, 2017
2 parents 02f0c83 + b3f22a7 commit 5045c6c
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 12 deletions.
18 changes: 14 additions & 4 deletions dist/localforage.js
Expand Up @@ -343,7 +343,7 @@ if (typeof global.Promise !== 'function') {
},{"2":2}],4:[function(_dereq_,module,exports){
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

Expand Down Expand Up @@ -435,7 +435,9 @@ function createBlob(parts, properties) {

// This is CommonJS because lie is an external dependency, so Rollup
// can just ignore it.
if (typeof Promise === 'undefined' && typeof _dereq_ !== 'undefined') {
if (typeof Promise === 'undefined') {
// In the "nopromises" build this will just throw if you don't have
// a global promise object, but it would throw anyway later.
_dereq_(3);
}
var Promise$1 = Promise;
Expand Down Expand Up @@ -1484,7 +1486,7 @@ function iterate$1(iterator, callback) {
return promise;
}

function setItem$1(key, value, callback) {
function _setItem(key, value, callback, retriesLeft) {
var self = this;

// Cast the key to a string, as that's all we can set as a key.
Expand Down Expand Up @@ -1526,7 +1528,11 @@ function setItem$1(key, value, callback) {
// more storage on Safari, this error will
// be called.
//
// TODO: Try to re-run the transaction.
// Try to re-run the transaction.
if (retriesLeft > 0) {
resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
return;
}
reject(sqlError);
}
});
Expand All @@ -1539,6 +1545,10 @@ function setItem$1(key, value, callback) {
return promise;
}

function setItem$1(key, value, callback) {
return _setItem.apply(this, [key, value, callback, 1]);
}

function removeItem$1(key, callback) {
var self = this;

Expand Down
2 changes: 1 addition & 1 deletion dist/localforage.min.js

Large diffs are not rendered by default.

18 changes: 14 additions & 4 deletions dist/localforage.nopromises.js
Expand Up @@ -7,7 +7,7 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.localforage = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw (f.code="MODULE_NOT_FOUND", f)}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

Expand Down Expand Up @@ -99,7 +99,9 @@ function createBlob(parts, properties) {

// This is CommonJS because lie is an external dependency, so Rollup
// can just ignore it.
if (typeof Promise === 'undefined' && typeof _dereq_ !== 'undefined') {
if (typeof Promise === 'undefined') {
// In the "nopromises" build this will just throw if you don't have
// a global promise object, but it would throw anyway later.
_dereq_('lie/polyfill');
}
var Promise$1 = Promise;
Expand Down Expand Up @@ -1148,7 +1150,7 @@ function iterate$1(iterator, callback) {
return promise;
}

function setItem$1(key, value, callback) {
function _setItem(key, value, callback, retriesLeft) {
var self = this;

// Cast the key to a string, as that's all we can set as a key.
Expand Down Expand Up @@ -1190,7 +1192,11 @@ function setItem$1(key, value, callback) {
// more storage on Safari, this error will
// be called.
//
// TODO: Try to re-run the transaction.
// Try to re-run the transaction.
if (retriesLeft > 0) {
resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
return;
}
reject(sqlError);
}
});
Expand All @@ -1203,6 +1209,10 @@ function setItem$1(key, value, callback) {
return promise;
}

function setItem$1(key, value, callback) {
return _setItem.apply(this, [key, value, callback, 1]);
}

function removeItem$1(key, callback) {
var self = this;

Expand Down
2 changes: 1 addition & 1 deletion dist/localforage.nopromises.min.js

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions examples/dbquota-error.html
@@ -0,0 +1,56 @@
<!doctype html>
<html>
<head>
<meta charset="utf8" />
<title>Simple localForage example</title>
</head>
<body>
<script src="../dist/localforage.js"></script>
<script>
// Forcing WEBSQL here. Feel free to switch to other drivers :)
localforage.setDriver(localforage.WEBSQL).then(function() {
return localforage.ready();
}).then(() => {
console.log('ready: ' + localforage.driver());
}).then(() => {
var overall = Promise.resolve();
for (let i = 0; i < 1; i++) {
overall = overall.then(() => {
return wait(2000).then(() => (+new Date()) / 1000 | 0);
}).then((timestamp) => {
var data = new Uint32Array(2*1024*1024);
data[0] = timestamp;
return localforage.setItem(`bigdata${i}`, data).then(
data => console.log(`setItem${i} resolved`, data[0]),
e => {
console.log(`setItem${i} rejected`, e);
return Promise.reject(e);
}).then(() => {
return localforage.getItem(`bigdata${i}`);
}).then(
data => {
if (data && data[0] === timestamp) {
console.log(`getItem${i} data matches`, data[0]);
} else {
console.log(`getItem${i} data missmatch`, timestamp, data[0]);
}
},
e => {
console.log('rejected', e);
return Promise.reject(e);
});
});
}
return overall;
}).then(
() => console.log('all done'),
e => console.log('Error', e));

function wait(ms) {
return new Promise(function(resolve) {
resolve();
});
}
</script>
</body>
</html>
12 changes: 10 additions & 2 deletions src/drivers/websql.js
Expand Up @@ -137,7 +137,7 @@ function iterate(iterator, callback) {
return promise;
}

function setItem(key, value, callback) {
function _setItem(key, value, callback, retriesLeft) {
var self = this;

// Cast the key to a string, as that's all we can set as a key.
Expand Down Expand Up @@ -183,7 +183,11 @@ function setItem(key, value, callback) {
// more storage on Safari, this error will
// be called.
//
// TODO: Try to re-run the transaction.
// Try to re-run the transaction.
if (retriesLeft > 0) {
resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
return;
}
reject(sqlError);
}
});
Expand All @@ -196,6 +200,10 @@ function setItem(key, value, callback) {
return promise;
}

function setItem(key, value, callback) {
return _setItem.apply(this, [key, value, callback, 1]);
}

function removeItem(key, callback) {
var self = this;

Expand Down
52 changes: 52 additions & 0 deletions test/test.api.js
Expand Up @@ -266,6 +266,58 @@ DRIVERS.forEach(function(driverName) {
});
}

if (driverName === localforage.WEBSQL) {
describe('on QUOTA ERROR', function() {
var transaction;
var called;
var db;

function getQuotaErrorCode(transaction) {
return new Promise(function(resolve) {
transaction(function(t) {
t.executeSql('');
}, function(err) {
resolve(err.QUOTA_ERR);
});
}).catch(function(err) {
return err.QUOTA_ERR;
});
}

beforeEach(function() {
called = 0;
db = localforage._dbInfo.db;
transaction = db.transaction;

db.transaction = function(fn, errFn) {
called += 1;
// restore the normal transaction,
// so that subsequent operations work
db.transaction = transaction;

getQuotaErrorCode(transaction).then(function(QUOTA_ERR) {
var error = new Error();
error.code = QUOTA_ERR;
errFn(error);
});
};
});

it('should retry setItem', function(done) {
localforage.setItem('key', {}).then(function() {
expect(called).to.be(1);
done();
}, function(error) {
done(error || 'error');
});
});

after(function() {
db.transaction = transaction || db.transaction;
});
});
}

it('should iterate [callback]', function(done) {
localforage.setItem('officeX', 'InitechX', function(err, setValue) {
expect(setValue).to.be('InitechX');
Expand Down

0 comments on commit 5045c6c

Please sign in to comment.