layered-loader
Version:
Data loader with support for caching and fallback data sources
148 lines • 6.21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractGroupCache = void 0;
const AbstractCache_1 = require("./AbstractCache");
class AbstractGroupCache extends AbstractCache_1.AbstractCache {
isGroupCache() {
return true;
}
async invalidateCacheForGroup(group) {
if (this.asyncCache) {
await this.asyncCache.deleteGroup(group).catch((err) => {
this.cacheUpdateErrorHandler(err, `group: ${group}`, this.asyncCache, this.logger);
});
}
this.inMemoryCache.deleteGroup(group);
this.runningLoads.delete(group);
if (this.notificationPublisher) {
void this.notificationPublisher.deleteGroup(group).catch((err) => {
this.notificationPublisher.errorHandler(err, this.notificationPublisher.channel, this.logger);
});
}
}
getInMemoryOnly(loadParams, group) {
const key = this.cacheKeyFromLoadParamsResolver(loadParams);
if (this.inMemoryCache.ttlLeftBeforeRefreshInMsecs) {
const groupLoads = this.resolveGroupLoads(group);
if (!groupLoads.has(key)) {
const expirationTime = this.inMemoryCache.getExpirationTimeFromGroup(key, group);
if (expirationTime && expirationTime - Date.now() < this.inMemoryCache.ttlLeftBeforeRefreshInMsecs) {
void this.getAsyncOnly(loadParams, group);
}
}
}
return this.inMemoryCache.getFromGroup(key, group);
}
getManyInMemoryOnly(keys, group) {
// Note that it doesn't support preemptive refresh
return this.inMemoryCache.getManyFromGroup(keys, group);
}
getAsyncOnly(loadParams, group) {
const key = this.cacheKeyFromLoadParamsResolver(loadParams);
const groupLoads = this.resolveGroupLoads(group);
const existingLoad = groupLoads.get(key);
if (existingLoad) {
return existingLoad;
}
const loadingPromise = this.resolveGroupValue(key, group, loadParams);
groupLoads.set(key, loadingPromise);
loadingPromise
.then((resolvedValue) => {
if (resolvedValue !== undefined) {
this.inMemoryCache.setForGroup(key, resolvedValue, group);
}
this.deleteGroupRunningLoad(groupLoads, group, key);
})
.catch(() => {
this.deleteGroupRunningLoad(groupLoads, group, key);
});
return loadingPromise;
}
getManyAsyncOnly(keys, group, loadParams) {
// This doesn't support deduplication, and never might, as that would affect perf strongly. Maybe as an opt-in option in the future?
return this.resolveManyGroupValues(keys, group, loadParams).then((result) => {
for (let i = 0; i < result.resolvedValues.length; i++) {
const resolvedValue = result.resolvedValues[i];
const id = this.cacheKeyFromValueResolver(resolvedValue);
this.inMemoryCache.setForGroup(id, resolvedValue, group);
}
return result;
});
}
get(loadParams, group) {
const key = this.cacheKeyFromLoadParamsResolver(loadParams);
const inMemoryValue = this.getInMemoryOnly(loadParams, group);
if (inMemoryValue !== undefined) {
return Promise.resolve(inMemoryValue);
}
return this.getAsyncOnly(loadParams, group);
}
getMany(keys, group, loadParams) {
const inMemoryValues = this.getManyInMemoryOnly(keys, group);
// everything is in memory, hurray
if (inMemoryValues.unresolvedKeys.length === 0) {
return Promise.resolve(inMemoryValues.resolvedValues);
}
return this.getManyAsyncOnly(inMemoryValues.unresolvedKeys, group, loadParams).then((asyncRetrievedValues) => {
return [...inMemoryValues.resolvedValues, ...asyncRetrievedValues.resolvedValues];
});
}
async invalidateCacheFor(key, group) {
this.inMemoryCache.deleteFromGroup(key, group);
if (this.asyncCache) {
await this.asyncCache.deleteFromGroup(key, group).catch((err) => {
this.cacheUpdateErrorHandler(err, undefined, this.asyncCache, this.logger);
});
}
const groupLoads = this.resolveGroupLoads(group);
this.deleteGroupRunningLoad(groupLoads, group, key);
if (this.notificationPublisher) {
void this.notificationPublisher.deleteFromGroup(key, group).catch((err) => {
this.notificationPublisher.errorHandler(err, this.notificationPublisher.channel, this.logger);
});
}
}
async resolveGroupValue(key, group, _loadParams) {
if (this.asyncCache) {
const cachedValue = await this.asyncCache.getFromGroup(key, group).catch((err) => {
this.loadErrorHandler(err, key, this.asyncCache, this.logger);
});
if (cachedValue !== undefined) {
return cachedValue;
}
}
return undefined;
}
async resolveManyGroupValues(keys, group, _loadParams) {
if (this.asyncCache) {
return this.asyncCache.getManyFromGroup(keys, group).catch((err) => {
this.loadErrorHandler(err, keys.toString(), this.asyncCache, this.logger);
return {
unresolvedKeys: keys,
resolvedValues: [],
};
});
}
return {
unresolvedKeys: keys,
resolvedValues: [],
};
}
resolveGroupLoads(group) {
const load = this.runningLoads.get(group);
if (load) {
return load;
}
const loadCache = new Map();
this.runningLoads.set(group, loadCache);
return loadCache;
}
deleteGroupRunningLoad(groupLoads, group, key) {
groupLoads.delete(key);
if (groupLoads.size === 0) {
this.runningLoads.delete(group);
}
}
}
exports.AbstractGroupCache = AbstractGroupCache;
//# sourceMappingURL=AbstractGroupCache.js.map