UNPKG

rxdb

Version:

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

224 lines (219 loc) 7.69 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.DocumentCache = void 0; exports.mapDocumentsDataToCacheDocs = mapDocumentsDataToCacheDocs; var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _index2 = require("./plugins/utils/index.js"); var _overwritable = require("./overwritable.js"); /** * Because we have to create many cache items, * we use an array instead of an object with properties * for better performance and less memory usage. * @link https://stackoverflow.com/questions/17295056/array-vs-object-efficiency-in-javascript */ /** * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry */ /** * The DocumentCache stores RxDocument objects * by their primary key and revision. * This is useful on client side applications where * it is not known how much memory can be used, so * we de-duplicate RxDocument states to save memory. * To not fill up the memory with old document states, the DocumentCache * only contains weak references to the RxDocuments themself. * @link https://caniuse.com/?search=weakref */ var DocumentCache = exports.DocumentCache = /*#__PURE__*/function () { /** * Process stuff lazy to not block the CPU * on critical paths. */ /** * Some JavaScript runtimes like QuickJS, * so not have a FinalizationRegistry or WeakRef. * Therefore we need a workaround which might waste a lot of memory, * but at least works. */ function DocumentCache(primaryPath, changes$, /** * A method that can create a RxDocument by the given document data. */ documentCreator) { this.cacheItemByDocId = new Map(); this.tasks = new Set(); this.registry = typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(docMeta => { var docId = docMeta.docId; var cacheItem = this.cacheItemByDocId.get(docId); if (cacheItem) { cacheItem[0].delete(docMeta.revisionHeight); if (cacheItem[0].size === 0) { /** * No state of the document is cached anymore, * so we can clean up. */ this.cacheItemByDocId.delete(docId); } } }) : undefined; this.primaryPath = primaryPath; this.changes$ = changes$; this.documentCreator = documentCreator; changes$.subscribe(events => { this.tasks.add(() => { var cacheItemByDocId = this.cacheItemByDocId; for (var index = 0; index < events.length; index++) { var event = events[index]; var cacheItem = cacheItemByDocId.get(event.documentId); if (cacheItem) { var documentData = event.documentData; if (!documentData) { documentData = event.previousDocumentData; } cacheItem[1] = documentData; } } }); if (this.tasks.size <= 1) { (0, _index2.requestIdlePromiseNoQueue)().then(() => { this.processTasks(); }); } }); } var _proto = DocumentCache.prototype; _proto.processTasks = function processTasks() { if (this.tasks.size === 0) { return; } var tasks = Array.from(this.tasks); tasks.forEach(task => task()); this.tasks.clear(); } /** * Get the RxDocument from the cache * and create a new one if not exits before. * @overwrites itself with the actual function * because this is @performance relevant. * It is called on each document row for each write and read. */; /** * Throws if not exists */ _proto.getLatestDocumentData = function getLatestDocumentData(docId) { this.processTasks(); var cacheItem = (0, _index2.getFromMapOrThrow)(this.cacheItemByDocId, docId); return cacheItem[1]; }; _proto.getLatestDocumentDataIfExists = function getLatestDocumentDataIfExists(docId) { this.processTasks(); var cacheItem = this.cacheItemByDocId.get(docId); if (cacheItem) { return cacheItem[1]; } }; return (0, _createClass2.default)(DocumentCache, [{ key: "getCachedRxDocuments", get: function () { var fn = getCachedRxDocumentMonad(this); return (0, _index2.overwriteGetterForCaching)(this, 'getCachedRxDocuments', fn); } }, { key: "getCachedRxDocument", get: function () { var fn = getCachedRxDocumentMonad(this); return (0, _index2.overwriteGetterForCaching)(this, 'getCachedRxDocument', doc => fn([doc])[0]); } }]); }(); /** * This function is called very very often. * This is likely the most important function for RxDB overall performance * @hotPath This is one of the most important methods for performance. * It is used in many places to transform the raw document data into RxDocuments. */ function getCachedRxDocumentMonad(docCache) { var primaryPath = docCache.primaryPath; var cacheItemByDocId = docCache.cacheItemByDocId; var registry = docCache.registry; var deepFreezeWhenDevMode = _overwritable.overwritable.deepFreezeWhenDevMode; var documentCreator = docCache.documentCreator; var fn = docsData => { var ret = new Array(docsData.length); var registryTasks = []; for (var index = 0; index < docsData.length; index++) { var docData = docsData[index]; var docId = docData[primaryPath]; var revisionHeight = (0, _index2.getHeightOfRevision)(docData._rev); var byRev = void 0; var cachedRxDocumentWeakRef = void 0; var cacheItem = cacheItemByDocId.get(docId); if (!cacheItem) { byRev = new Map(); cacheItem = [byRev, docData]; cacheItemByDocId.set(docId, cacheItem); } else { byRev = cacheItem[0]; cachedRxDocumentWeakRef = byRev.get(revisionHeight); } var cachedRxDocument = cachedRxDocumentWeakRef ? cachedRxDocumentWeakRef.deref() : undefined; if (!cachedRxDocument) { docData = deepFreezeWhenDevMode(docData); cachedRxDocument = documentCreator(docData); byRev.set(revisionHeight, createWeakRefWithFallback(cachedRxDocument)); if (registry) { registryTasks.push(cachedRxDocument); } } ret[index] = cachedRxDocument; } if (registryTasks.length > 0 && registry) { /** * Calling registry.register() has shown to have * really bad performance. So we add the cached documents * lazily. */ docCache.tasks.add(() => { for (var _index = 0; _index < registryTasks.length; _index++) { var doc = registryTasks[_index]; registry.register(doc, { docId: doc.primary, revisionHeight: (0, _index2.getHeightOfRevision)(doc.revision) }); } }); if (docCache.tasks.size <= 1) { (0, _index2.requestIdlePromiseNoQueue)().then(() => { docCache.processTasks(); }); } } return ret; }; return fn; } function mapDocumentsDataToCacheDocs(docCache, docsData) { var getCachedRxDocuments = docCache.getCachedRxDocuments; return getCachedRxDocuments(docsData); } /** * Fallback for JavaScript runtimes that do not support WeakRef. * The fallback will keep the items in cache forever, * but at least works. */ var HAS_WEAK_REF = typeof WeakRef === 'function'; var createWeakRefWithFallback = HAS_WEAK_REF ? createWeakRef : createWeakRefFallback; function createWeakRef(obj) { return new WeakRef(obj); } function createWeakRefFallback(obj) { return { deref() { return obj; } }; } //# sourceMappingURL=doc-cache.js.map