Commit 5a302480 authored by Tristan Cavelier's avatar Tristan Cavelier

get,post,put,remove indexexDB performance improved

parent b60b42a1
...@@ -81,33 +81,36 @@ ...@@ -81,33 +81,36 @@
} }
// XXX doc string // XXX doc string
function openRequestOnUpgradeNeeded(shared, event) {
if (shared.aborted) { return; }
shared.db = event.target.result;
try {
shared.store = shared.db.createObjectStore("metadata", {
"keyPath": "_id"
//"autoIncrement": true
});
// `createObjectStore` can throw InvalidStateError - open_req.onerror
// and db.onerror won't be called.
shared.store.createIndex("_id", "_id");
// `store.createIndex` can throw an error
shared.db_created = true;
shared.store.transaction.oncomplete = function () {
delete shared.store;
};
} catch (e) {
shared.reject(e);
shared.db.close();
shared.aborted = true;
}
}
IndexedDBStorage.prototype.createDBIfNecessary = function () { IndexedDBStorage.prototype.createDBIfNecessary = function () {
var status, open_req = indexedDB.open(this._database_name); var shared = {}, open_req = indexedDB.open(this._database_name);
// No request.abort() is provided so we cannot cancel database creation // No request.abort() is provided so we cannot cancel database creation
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
open_req.onupgradeneeded = function () { shared.reject = reject;
// *Called at t + 1* open_req.onupgradeneeded =
var store, db = open_req.result; openRequestOnUpgradeNeeded.bind(open_req, shared);
// If we reach this point, the database is created.
// There is no way to cancel the operation from here.
// So let's continue.
try {
store = db.createObjectStore("metadata", {
"keyPath": "_id"
});
// `createObjectStore` can throw InvalidStateError - open_req.onerror
// and db.onerror won't be called.
store.createIndex("_id", "_id");
// `store.createIndex` can throw an error
status = "created";
// store.transaction.oncomplete = function () {
// // *Called at t + 2*
// };
} catch (e) {
reject(e);
db.close();
}
};
open_req.onerror = function () { open_req.onerror = function () {
if (open_req.result) { open_req.result.close(); } if (open_req.result) { open_req.result.close(); }
reject(open_req.error); reject(open_req.error);
...@@ -115,7 +118,7 @@ ...@@ -115,7 +118,7 @@
open_req.onsuccess = function () { open_req.onsuccess = function () {
// *Called at t + 3* // *Called at t + 3*
open_req.result.close(); open_req.result.close();
resolve(status || "no_content"); resolve(shared.db_created ? "created" : "no_content");
}; };
}); });
}; };
...@@ -280,86 +283,256 @@ ...@@ -280,86 +283,256 @@
// XXX doc string // XXX doc string
IndexedDBStorage.prototype.get = function (command, param) { IndexedDBStorage.prototype.get = function (command, param) {
this.createDBIfNecessary(). var shared = {"connector": this};
then(this.getMetadata.bind(this, param._id)). new Promise(function (resolve, reject) {
then(function (data) { shared.reject = reject;
command.success({"data": data});
}, command.error, command.notify); // Open DB //
var open_req = indexedDB.open(shared.connector._database_name);
open_req.onerror = function (event) {
reject(event.target.errorCode);
};
// Create DB if necessary //
open_req.onupgradeneeded =
openRequestOnUpgradeNeeded.bind(open_req, shared);
open_req.onsuccess = function (event) {
if (shared.aborted) { return; }
try {
shared.db = event.target.result;
// Open transaction //
shared.tx = shared.db.transaction("metadata", "readonly");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index_request = shared.store.index("_id");
// Get metadata //
shared.index_request.get(param._id).onsuccess = function (event) {
if (shared.aborted) { return; }
if (event.target.result === undefined) {
reject({"status": 404});
return;
}
shared.final_result = {"data": event.target.result};
};
// Respond to jIO //
shared.tx.oncomplete = function () {
resolve(shared.final_result);
shared.db.close();
};
} catch (e1) {
reject(e1);
shared.db.close();
}
};
}).then(command.success, command.error, command.notify);
}; };
// XXX doc string // XXX doc string
IndexedDBStorage.prototype.post = function (command, metadata) { IndexedDBStorage.prototype.post = function (command, metadata) {
var promise; var shared = {"connector": this};
promise = this.createDBIfNecessary(); if (!metadata._id) {
if (metadata._id) {
promise = promise.
then(this.getMetadata.bind(this, metadata._id)).
then(function () {
throw "conflict";
}, function (error) {
if (error === "not_found") {
return;
}
throw error;
});
} else {
metadata._id = generateUuid(); metadata._id = generateUuid();
} }
promise.then(this.putMetadata.bind(this, metadata)). new Promise(function (resolve, reject) {
then(function () { shared.reject = reject;
command.success({"id": metadata._id});
}, command.error, command.notify); // Open DB //
var open_req = indexedDB.open(shared.connector._database_name);
open_req.onerror = function (event) {
reject(event.target.errorCode);
};
// Create DB if necessary //
open_req.onupgradeneeded =
openRequestOnUpgradeNeeded.bind(open_req, shared);
open_req.onsuccess = function (event) {
if (shared.aborted) { return; }
try {
shared.db = event.target.result;
// Open transaction //
shared.tx = shared.db.transaction("metadata", "readwrite");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index_request = shared.store.index("_id");
// Get metadata //
shared.index_request.get(metadata._id).onsuccess = function (event) {
if (shared.aborted) { return; }
if (event.target.result !== undefined) {
shared.db.close();
reject({"status": 409, "reason": "document already exist"});
return;
}
delete metadata._attachments;
// Push metadata //
shared.store.put(metadata);
};
shared.tx.oncomplete = function () {
// Respond to jIO //
shared.db.close();
resolve({"id": metadata._id});
};
} catch (e1) {
reject(e1);
shared.db.close();
}
};
}).then(command.success, command.error, command.notify);
}; };
// XXX doc string // XXX doc string
IndexedDBStorage.prototype.put = function (command, metadata) { IndexedDBStorage.prototype.put = function (command, metadata) {
var status; var shared = {"connector": this};
this.createDBIfNecessary(). new Promise(function (resolve, reject) {
then(this.getMetadata.bind(this, metadata._id)). shared.reject = reject;
then(null, function (error) {
if (error === "not_found") { // Open DB //
status = "created"; var open_req = indexedDB.open(shared.connector._database_name);
return {}; open_req.onerror = function (event) {
} reject(event.target.errorCode);
throw error; };
}).
then(function (data) { // Create DB if necessary //
var i, l, key, array; open_req.onupgradeneeded =
array = Object.keys(metadata); openRequestOnUpgradeNeeded.bind(open_req, shared);
for (i = 0, l = array.length; i < l; i += 1) {
key = array[i]; open_req.onsuccess = function (event) {
metadata[key] = metadataObjectToString(metadata[key]); if (shared.aborted) { return; }
} try {
if (data._attachments) { shared.db = event.target.result;
metadata._attachments = data._attachments; // Open transaction //
shared.tx = shared.db.transaction("metadata", "readwrite");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index = shared.store.index("_id");
// Get metadata //
shared.index.get(metadata._id).onsuccess = function (event) {
if (shared.aborted) { return; }
var i, l, key, array, data;
if (event.target.result !== undefined) {
shared.found = true;
data = event.target.result;
// Update metadata //
array = Object.keys(metadata);
for (i = 0, l = array.length; i < l; i += 1) {
key = array[i];
metadata[key] = metadataObjectToString(metadata[key]);
}
if (data._attachments) {
metadata._attachments = data._attachments;
}
} else {
delete metadata._attachments;
}
// Push metadata //
shared.store.put(metadata);
};
shared.tx.oncomplete = function () {
// Respond to jIO //
shared.db.close();
resolve({"status": shared.found ? 204 : 201});
};
} catch (e1) {
reject(e1);
shared.db.close();
} }
}). };
then(this.putMetadata.bind(this, metadata)). }).then(command.success, command.error, command.notify);
then(function () {
command.success(status || "no_content");
}, command.error, command.notify);
}; };
// XXX doc string // XXX doc string
IndexedDBStorage.prototype.remove = function (command, param) { IndexedDBStorage.prototype.remove = function (command, param) {
var this_ = this; var shared = {"connector": this};
this.createDBIfNecessary(). new Promise(function (resolve, reject) {
then(this.getMetadata.bind(this, param._id)). shared.reject = reject;
then(function (metadata) {
var attachments = metadata._attachments, promise; // Open DB //
promise = this_.removeMetadata(param._id); var open_req = indexedDB.open(shared.connector._database_name);
if (typeof attachments === "object" && attachments !== null) { open_req.onerror = function (event) {
promise.then(function (answer) { reject(event.target.errorCode);
return this_.removeAttachments( };
param._id,
Object.keys(attachments) // Create DB if necessary //
).then(null, function () { return; }). open_req.onupgradeneeded =
then(function () { return answer; }); openRequestOnUpgradeNeeded.bind(open_req, shared);
});
open_req.onsuccess = function (event) {
if (shared.aborted) { return; }
try {
shared.db = event.target.result;
// Open transaction //
shared.tx = shared.db.transaction("metadata", "readwrite");
shared.tx.onerror = function () {
reject(shared.tx.error);
shared.db.close();
};
shared.onCancel = function () {
shared.tx.abort();
shared.db.close();
};
// Get index //
shared.store = shared.tx.objectStore("metadata");
shared.index = shared.store.index("_id");
// Get metadata //
shared.index.get(param._id).onsuccess = function (event) {
if (shared.aborted) { return; }
if (event.target.result === undefined) {
shared.db.close();
reject({"status": 404});
return;
}
// Delete metadata //
shared.store["delete"](param._id);
// XXX Delete attachments //
};
shared.tx.oncomplete = function () {
// Respond to jIO //
shared.db.close();
resolve();
};
} catch (e1) {
reject(e1);
shared.db.close();
} }
return promise; };
}). }).then(command.success, command.error, command.notify);
then(command.success, command.error, command.notify);
}; };
// XXX doc string // XXX doc string
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment