rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
99 lines (95 loc) • 3.64 kB
JavaScript
/**
* the query-cache makes sure that on every query-state, exactly one instance can exist
* if you use the same mango-query more then once, it will reuse the first RxQuery
*/
import { getFromMapOrCreate, nextTick, now, requestIdlePromise } from "./plugins/utils/index.js";
export var QueryCache = /*#__PURE__*/function () {
function QueryCache() {
this._map = new Map();
}
var _proto = QueryCache.prototype;
/**
* check if an equal query is in the cache,
* if true, return the cached one,
* if false, save the given one and return it
*/
_proto.getByQuery = function getByQuery(rxQuery) {
var stringRep = rxQuery.toString();
var ret = getFromMapOrCreate(this._map, stringRep, () => rxQuery);
return ret;
};
return QueryCache;
}();
export function createQueryCache() {
return new QueryCache();
}
export function uncacheRxQuery(queryCache, rxQuery) {
rxQuery.uncached = true;
var stringRep = rxQuery.toString();
queryCache._map.delete(stringRep);
}
export function countRxQuerySubscribers(rxQuery) {
return rxQuery.refCount$.observers.length;
}
export var DEFAULT_TRY_TO_KEEP_MAX = 100;
export var DEFAULT_UNEXECUTED_LIFETIME = 30 * 1000;
/**
* The default cache replacement policy
* See docs-src/query-cache.md to learn how it should work.
* Notice that this runs often and should block the cpu as less as possible
* This is a monad which makes it easier to unit test
*/
export var defaultCacheReplacementPolicyMonad = (tryToKeepMax, unExecutedLifetime) => (_collection, queryCache) => {
if (queryCache._map.size < tryToKeepMax) {
return;
}
var minUnExecutedLifetime = now() - unExecutedLifetime;
var maybeUncache = [];
var queriesInCache = Array.from(queryCache._map.values());
for (var rxQuery of queriesInCache) {
// filter out queries with subscribers
if (countRxQuerySubscribers(rxQuery) > 0) {
continue;
}
// directly uncache queries that never executed and are older than unExecutedLifetime
if (rxQuery._lastEnsureEqual === 0 && rxQuery._creationTime < minUnExecutedLifetime) {
uncacheRxQuery(queryCache, rxQuery);
continue;
}
maybeUncache.push(rxQuery);
}
var mustUncache = maybeUncache.length - tryToKeepMax;
if (mustUncache <= 0) {
return;
}
var sortedByLastUsage = maybeUncache.sort((a, b) => a._lastEnsureEqual - b._lastEnsureEqual);
var toRemove = sortedByLastUsage.slice(0, mustUncache);
toRemove.forEach(rxQuery => uncacheRxQuery(queryCache, rxQuery));
};
export var defaultCacheReplacementPolicy = defaultCacheReplacementPolicyMonad(DEFAULT_TRY_TO_KEEP_MAX, DEFAULT_UNEXECUTED_LIFETIME);
export var COLLECTIONS_WITH_RUNNING_CLEANUP = new WeakSet();
/**
* Triggers the cache replacement policy after waitTime has passed.
* We do not run this directly because at exactly the time a query is created,
* we need all CPU to minimize latency.
* Also this should not be triggered multiple times when waitTime is still waiting.
*/
export function triggerCacheReplacement(rxCollection) {
if (COLLECTIONS_WITH_RUNNING_CLEANUP.has(rxCollection)) {
// already started
return;
}
COLLECTIONS_WITH_RUNNING_CLEANUP.add(rxCollection);
/**
* Do not run directly to not reduce result latency of a new query
*/
nextTick() // wait at least one tick
.then(() => requestIdlePromise(200)) // and then wait for the CPU to be idle
.then(() => {
if (!rxCollection.closed) {
rxCollection.cacheReplacementPolicy(rxCollection, rxCollection._queryCache);
}
COLLECTIONS_WITH_RUNNING_CLEANUP.delete(rxCollection);
});
}
//# sourceMappingURL=query-cache.js.map