@nozbe/lokijs
Advanced tools
Comparing version 1.5.11-wmelon-idb-fix to 1.5.11-wmelon-idb-fix2
{ | ||
"name": "@nozbe/lokijs", | ||
"version": "1.5.11-wmelon-idb-fix", | ||
"version": "1.5.11-wmelon-idb-fix2", | ||
"description": "Nozbe's temporary fork of LokiJS - used for WatermelonDB purposes to work around NPM issues", | ||
@@ -5,0 +5,0 @@ "homepage": "https://techfort.github.io/LokiJS/", |
@@ -40,2 +40,5 @@ (function(root, factory) { | ||
* Use this as an opportunity to execute code concurrently while IDB does work on a separate thread | ||
* @param {function} options.onDidOverwrite Called when this adapter is forced to overwrite contents | ||
* of IndexedDB. This happens if there's another open tab of the same app that's making changes. | ||
* You might use it as an opportunity to alert user to the potential loss of data | ||
* @param {function} options.serializeChunk Called with a chunk (array of Loki documents) before | ||
@@ -165,65 +168,89 @@ * it's saved to IndexedDB. You can use it to manually compress on-disk representation | ||
var updatePrevVersionIds = function () { | ||
console.error('Unexpected successful tx - cannot update previous version ids'); | ||
}; | ||
// try..catch is required, e.g.: | ||
// InvalidStateError: Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing. | ||
// (this may happen if another tab has called deleteDatabase) | ||
try { | ||
var updatePrevVersionIds = function () { | ||
console.error('Unexpected successful tx - cannot update previous version ids'); | ||
}; | ||
var didOverwrite = false; | ||
DEBUG && console.log("save tx: begin"); | ||
var tx = this.idb.transaction(['LokiIncrementalData'], "readwrite"); | ||
tx.oncomplete = function() { | ||
updatePrevVersionIds(); | ||
DEBUG && console.log("save tx: complete"); | ||
return finish(); | ||
}; | ||
var tx = this.idb.transaction(['LokiIncrementalData'], "readwrite"); | ||
tx.oncomplete = function() { | ||
updatePrevVersionIds(); | ||
finish(); | ||
if (didOverwrite && that.options.onDidOverwrite) { | ||
that.options.onDidOverwrite(); | ||
} | ||
}; | ||
tx.onerror = function(e) { | ||
DEBUG && console.log("save tx: error"); | ||
return finish(e); | ||
}; | ||
tx.onerror = function(e) { | ||
finish(e); | ||
}; | ||
tx.onabort = function(e) { | ||
DEBUG && console.log("save tx: abort"); | ||
return finish(e); | ||
}; | ||
tx.onabort = function(e) { | ||
finish(e); | ||
}; | ||
var store = tx.objectStore('LokiIncrementalData'); | ||
var store = tx.objectStore('LokiIncrementalData'); | ||
function performSave(maxChunkIds) { | ||
var incremental = !maxChunkIds; | ||
var chunkInfo = that._putInChunks(store, getLokiCopy(), incremental, maxChunkIds); | ||
updatePrevVersionIds = function() { | ||
that._prevLokiVersionId = chunkInfo.lokiVersionId; | ||
chunkInfo.collectionVersionIds.forEach(function (collectionInfo) { | ||
that._prevCollectionVersionIds[collectionInfo.name] = collectionInfo.versionId; | ||
var performSave = function (maxChunkIds) { | ||
try { | ||
var incremental = !maxChunkIds; | ||
var chunkInfo = that._putInChunks(store, getLokiCopy(), incremental, maxChunkIds); | ||
// Update last seen version IDs, but only after the transaction is successful | ||
updatePrevVersionIds = function() { | ||
that._prevLokiVersionId = chunkInfo.lokiVersionId; | ||
chunkInfo.collectionVersionIds.forEach(function (collectionInfo) { | ||
that._prevCollectionVersionIds[collectionInfo.name] = collectionInfo.versionId; | ||
}); | ||
}; | ||
tx.commit && tx.commit(); | ||
} catch (error) { | ||
console.error('idb performSave failed: ', error); | ||
tx.abort(); | ||
} | ||
}; | ||
// Incrementally saving changed chunks breaks down if there is more than one writer to IDB | ||
// (multiple tabs of the same web app), leading to data corruption. To fix that, we save all | ||
// metadata chunks (loki + collections) with a unique ID on each save and remember it. Before | ||
// the subsequent save, we read loki from IDB to check if its version ID changed. If not, we're | ||
// guaranteed that persisted DB is consistent with our diff. Otherwise, we fall back to the slow | ||
// path and overwrite *all* database chunks with our version. Both reading and writing must | ||
// happen in the same IDB transaction for this to work. | ||
// TODO: We can optimize the slow path by fetching collection metadata chunks and comparing their | ||
// version IDs with those last seen by us. Since any change in collection data requires a metadata | ||
// chunk save, we're guaranteed that if the IDs match, we don't need to overwrite chukns of this collection | ||
var getAllKeysThenSave = function() { | ||
// NOTE: We must fetch all keys to protect against a case where another tab has wrote more | ||
// chunks whan we did -- if so, we must delete them. | ||
idbReq(store.getAllKeys(), function(e) { | ||
var maxChunkIds = getMaxChunkIds(e.target.result); | ||
performSave(maxChunkIds); | ||
}, function(e) { | ||
console.error('Getting all keys failed: ', e); | ||
tx.abort(); | ||
}); | ||
}; | ||
DEBUG && console.log('chunks saved'); | ||
tx.commit && tx.commit(); | ||
} | ||
function getAllKeysThenSave() { | ||
idbReq(store.getAllKeys(), function(e) { | ||
var maxChunkIds = getMaxChunkIds(e.target.result); | ||
performSave(maxChunkIds); | ||
}, function(e) { | ||
console.error('Getting all keys failed: ', e); | ||
tx.abort(); | ||
}); | ||
} | ||
var getLokiThenSave = function() { | ||
idbReq(store.get('loki'), function(e) { | ||
if (lokiChunkVersionId(e.target.result) === that._prevLokiVersionId) { | ||
performSave(); | ||
} else { | ||
DEBUG && console.warn('Another writer changed Loki IDB, using slow path...'); | ||
didOverwrite = true; | ||
getAllKeysThenSave(); | ||
} | ||
}, function(e) { | ||
console.error('Getting loki chunk failed: ', e); | ||
tx.abort(); | ||
}); | ||
}; | ||
function getLokiThenSave() { | ||
idbReq(store.get('loki'), function(e) { | ||
if (lokiChunkVersionId(e.target.result) === that._prevLokiVersionId) { | ||
performSave(); | ||
} else { | ||
DEBUG && console.warn('--------> LOKI CHANGED!!! [slow path]'); | ||
// TODO: Get collection metadata chunks | ||
getAllKeysThenSave(); | ||
} | ||
}, function(e) { | ||
console.error('Getting loki chunk failed: ', e); | ||
tx.abort(); | ||
}); | ||
getLokiThenSave(); | ||
} catch (error) { | ||
finish(error); | ||
} | ||
getLokiThenSave(); | ||
}; | ||
@@ -290,2 +317,3 @@ | ||
savedSize += chunkData.length; | ||
DEBUG && incremental && console.log('Saving: ' + collection.name + ".chunk." + chunkId); | ||
idbStore.put({ | ||
@@ -325,2 +353,3 @@ key: collection.name + ".chunk." + chunkId, | ||
savedSize += metadataChunk.length; | ||
DEBUG && incremental && console.log('Saving: ' + collection.name + ".metadata"); | ||
idbStore.put({ | ||
@@ -341,2 +370,3 @@ key: collection.name + ".metadata", | ||
DEBUG && incremental && console.log('Saving: loki'); | ||
idbStore.put({ key: "loki", value: serializedMetadata }); | ||
@@ -539,2 +569,7 @@ | ||
db.onversionchange = function(versionChangeEvent) { | ||
// Ignore if database was deleted and recreated in the meantime | ||
if (that.idb !== db) { | ||
return; | ||
} | ||
DEBUG && console.log('IDB version change', versionChangeEvent); | ||
@@ -547,3 +582,4 @@ // This function will be called if another connection changed DB version | ||
// to force logout | ||
db.close(); | ||
that.idb.close(); | ||
that.idb = null; | ||
if (that.options.onversionchange) { | ||
@@ -580,11 +616,8 @@ that.options.onversionchange(versionChangeEvent); | ||
var request = tx.objectStore('LokiIncrementalData').getAll(); | ||
request.onsuccess = function(e) { | ||
idbReq(tx.objectStore('LokiIncrementalData').getAll(), function(e) { | ||
var chunks = e.target.result; | ||
callback(chunks); | ||
}; | ||
request.onerror = function(e) { | ||
}, function(e) { | ||
callback(e); | ||
}; | ||
}); | ||
@@ -682,3 +715,9 @@ if (this.options.onFetchStart) { | ||
function idbReq(request, onsuccess, onerror) { | ||
request.onsuccess = onsuccess; | ||
request.onsuccess = function (e) { | ||
try { | ||
return onsuccess(e); | ||
} catch (error) { | ||
onerror(error); | ||
} | ||
}; | ||
request.onerror = onerror; | ||
@@ -685,0 +724,0 @@ return request; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3068765
9919