rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
141 lines (133 loc) • 4.4 kB
JavaScript
/**
* a buffer-cache which holds the last X changeEvents of the collection
*/
import { filter } from 'rxjs/operators';
import { appendToArray, requestIdlePromiseNoQueue } from "./plugins/utils/index.js";
/**
* This buffer rembemers previous change events
* so that queries can use them on .exec()
* to calculate the new result set via event-reduce instead
* of running the query against the storage.
*/
export var ChangeEventBuffer = /*#__PURE__*/function () {
/**
* These properties are private to ensure they cannot
* be read without first processing the lazy tasks.
*/
/**
* array with changeEvents
* starts with oldest known event, ends with newest
*/
function ChangeEventBuffer(collection) {
this.subs = [];
this.counter = 0;
this.eventCounterMap = new WeakMap();
this.buffer = [];
this.limit = 100;
this.tasks = new Set();
this.collection = collection;
this.subs.push(this.collection.eventBulks$.pipe(filter(bulk => !bulk.isLocal)).subscribe(eventBulk => {
this.tasks.add(() => this._handleChangeEvents(eventBulk.events));
if (this.tasks.size <= 1) {
requestIdlePromiseNoQueue().then(() => {
this.processTasks();
});
}
}));
}
var _proto = ChangeEventBuffer.prototype;
_proto.processTasks = function processTasks() {
if (this.tasks.size === 0) {
return;
}
var tasks = Array.from(this.tasks);
tasks.forEach(task => task());
this.tasks.clear();
};
_proto._handleChangeEvents = function _handleChangeEvents(events) {
var counterBefore = this.counter;
this.counter = this.counter + events.length;
if (events.length > this.limit) {
this.buffer = events.slice(events.length * -1);
} else {
appendToArray(this.buffer, events);
this.buffer = this.buffer.slice(this.limit * -1);
}
var counterBase = counterBefore + 1;
var eventCounterMap = this.eventCounterMap;
for (var index = 0; index < events.length; index++) {
var event = events[index];
eventCounterMap.set(event, counterBase + index);
}
};
_proto.getCounter = function getCounter() {
this.processTasks();
return this.counter;
};
_proto.getBuffer = function getBuffer() {
this.processTasks();
return this.buffer;
}
/**
* gets the array-index for the given pointer
* @return arrayIndex which can be used to iterate from there. If null, pointer is out of lower bound
*/;
_proto.getArrayIndexByPointer = function getArrayIndexByPointer(pointer) {
this.processTasks();
var oldestEvent = this.buffer[0];
var oldestCounter = this.eventCounterMap.get(oldestEvent);
if (pointer < oldestCounter) return null; // out of bounds
var rest = pointer - oldestCounter;
return rest;
}
/**
* get all changeEvents which came in later than the pointer-event
* @return array with change-events. If null, pointer out of bounds
*/;
_proto.getFrom = function getFrom(pointer) {
this.processTasks();
var ret = [];
var currentIndex = this.getArrayIndexByPointer(pointer);
if (currentIndex === null)
// out of bounds
return null;
while (true) {
var nextEvent = this.buffer[currentIndex];
currentIndex++;
if (!nextEvent) {
return ret;
} else {
ret.push(nextEvent);
}
}
};
_proto.runFrom = function runFrom(pointer, fn) {
this.processTasks();
var ret = this.getFrom(pointer);
if (ret === null) {
throw new Error('out of bounds');
} else {
ret.forEach(cE => fn(cE));
}
}
/**
* no matter how many operations are done on one document,
* only the last operation has to be checked to calculate the new state
* this function reduces the events to the last ChangeEvent of each doc.
* This functionality is currently disabled. It is questionable if
* pre-merging the events would really be faster or actually slower.
*/;
_proto.reduceByLastOfDoc = function reduceByLastOfDoc(changeEvents) {
this.processTasks();
return changeEvents.slice(0);
};
_proto.close = function close() {
this.tasks.clear();
this.subs.forEach(sub => sub.unsubscribe());
};
return ChangeEventBuffer;
}();
export function createChangeEventBuffer(collection) {
return new ChangeEventBuffer(collection);
}
//# sourceMappingURL=change-event-buffer.js.map