UNPKG

rxdb

Version:

A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/

169 lines (164 loc) 5.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IncrementalWriteQueue = void 0; exports.findNewestOfDocumentStates = findNewestOfDocumentStates; exports.modifierFromPublicToInternal = modifierFromPublicToInternal; var _rxError = require("./rx-error.js"); var _index = require("./plugins/utils/index.js"); var _rxStorageHelper = require("./rx-storage-helper.js"); /** * The incremental write queue * batches up all incremental writes to a collection * so that performance can be improved by: * - Running only one write even when there are multiple modifications to the same document. * - Run all writes ins a single bulkWrite() call even when there are writes to many documents. */ var IncrementalWriteQueue = exports.IncrementalWriteQueue = /*#__PURE__*/function () { function IncrementalWriteQueue(storageInstance, primaryPath, // can be used to run hooks etc. preWrite, postWrite) { this.queueByDocId = new Map(); this.isRunning = false; this.storageInstance = storageInstance; this.primaryPath = primaryPath; this.preWrite = preWrite; this.postWrite = postWrite; } var _proto = IncrementalWriteQueue.prototype; _proto.addWrite = function addWrite(lastKnownDocumentState, modifier) { var docId = lastKnownDocumentState[this.primaryPath]; var ar = (0, _index.getFromMapOrCreate)(this.queueByDocId, docId, () => []); var ret = new Promise((resolve, reject) => { var item = { lastKnownDocumentState, modifier, resolve, reject }; (0, _index.ensureNotFalsy)(ar).push(item); this.triggerRun(); }); return ret; }; _proto.triggerRun = async function triggerRun() { if (this.isRunning === true || this.queueByDocId.size === 0) { // already running return; } this.isRunning = true; var writeRows = []; /** * 'take over' so that while the async functions runs, * new incremental updates could be added from the outside. */ var itemsById = this.queueByDocId; this.queueByDocId = new Map(); await Promise.all(Array.from(itemsById.entries()).map(async ([_docId, items]) => { var oldData = findNewestOfDocumentStates(items.map(i => i.lastKnownDocumentState)); var newData = oldData; for (var item of items) { try { newData = await item.modifier( /** * We have to clone() each time because the modifier * might throw while it already changed some properties * of the document. */ (0, _index.clone)(newData)); } catch (err) { item.reject(err); item.reject = () => {}; item.resolve = () => {}; } } try { await this.preWrite(newData, oldData); } catch (err) { /** * If the before-hooks fail, * we reject all of the writes because it is * not possible to determine which one is to blame. */ items.forEach(item => item.reject(err)); return; } writeRows.push({ previous: oldData, document: newData }); })); var writeResult = writeRows.length > 0 ? await this.storageInstance.bulkWrite(writeRows, 'incremental-write') : { error: [] }; // process success await Promise.all((0, _rxStorageHelper.getWrittenDocumentsFromBulkWriteResponse)(this.primaryPath, writeRows, writeResult).map(result => { var docId = result[this.primaryPath]; this.postWrite(result); var items = (0, _index.getFromMapOrThrow)(itemsById, docId); items.forEach(item => item.resolve(result)); })); // process errors writeResult.error.forEach(error => { var docId = error.documentId; var items = (0, _index.getFromMapOrThrow)(itemsById, docId); var isConflict = (0, _rxError.isBulkWriteConflictError)(error); if (isConflict) { // had conflict -> retry afterwards var ar = (0, _index.getFromMapOrCreate)(this.queueByDocId, docId, () => []); /** * Add the items back to this.queueByDocId * by maintaining the original order. */ items.reverse().forEach(item => { item.lastKnownDocumentState = (0, _index.ensureNotFalsy)(isConflict.documentInDb); (0, _index.ensureNotFalsy)(ar).unshift(item); }); } else { // other error -> must be thrown var rxError = (0, _rxError.rxStorageWriteErrorToRxError)(error); items.forEach(item => item.reject(rxError)); } }); this.isRunning = false; /** * Always trigger another run * because in between there might be new items * been added to the queue. */ return this.triggerRun(); }; return IncrementalWriteQueue; }(); function modifierFromPublicToInternal(publicModifier) { var ret = async docData => { var withoutMeta = (0, _index.stripMetaDataFromDocument)(docData); withoutMeta._deleted = docData._deleted; var modified = await publicModifier(withoutMeta); var reattachedMeta = Object.assign({}, modified, { _meta: docData._meta, _attachments: docData._attachments, _rev: docData._rev, _deleted: typeof modified._deleted !== 'undefined' ? modified._deleted : docData._deleted }); if (typeof reattachedMeta._deleted === 'undefined') { reattachedMeta._deleted = false; } return reattachedMeta; }; return ret; } function findNewestOfDocumentStates(docs) { var newest = docs[0]; var newestRevisionHeight = (0, _index.getHeightOfRevision)(newest._rev); docs.forEach(doc => { var height = (0, _index.getHeightOfRevision)(doc._rev); if (height > newestRevisionHeight) { newest = doc; newestRevisionHeight = height; } }); return newest; } //# sourceMappingURL=incremental-write.js.map