dla
Version:
node.js data loader with caching and support of lists
151 lines • 6.43 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const object_hash_1 = require("object-hash");
const cache_1 = require("./cache");
const collection_1 = require("./collection");
function unpackListData(data) {
return Array.isArray(data) ? {
items: data,
meta: undefined,
} : {
items: data.items,
meta: data.meta || undefined,
};
}
class ListableCollection extends collection_1.Collection {
constructor(options) {
super(options);
this.cowardListCache = true;
this.loadList = options.loadList;
this.invalidationTags = options.invalidationTags;
if (options.cowardListCache !== undefined) {
this.cowardListCache = options.cowardListCache;
}
if (options.listCache !== undefined) {
this.listCache = options.listCache;
}
else if (options.cache) {
this.listCache = new cache_1.PrefixCache('l:', options.cache);
}
if (options.invalidatorCache !== undefined) {
this.invalidatorCache = options.invalidatorCache;
}
else if (options.cache) {
this.invalidatorCache = new cache_1.PrefixCache('i:', options.cache);
}
}
getListWithMeta(filter) {
return __awaiter(this, void 0, void 0, function* () {
let loaded;
if (!this.listCache) {
loaded = unpackListData(yield this.loadList(filter));
}
else {
// try to load list data from cache
const now = Date.now();
const cacheKey = object_hash_1.MD5(filter);
let loadedFromCache = yield this.listCache.get(cacheKey);
const invalidationTags = this.invalidationTags ? this.invalidationTags(filter) : null;
if (loadedFromCache &&
invalidationTags &&
!(yield this.checkInvalidators(invalidationTags, loadedFromCache.ts))) {
loadedFromCache = null;
}
if (loadedFromCache) {
if (this.cowardListCache) {
// return cached list if all items stored in cache
// and no one item has been recached after list cache was built
const cachedObjects = yield this.objectCache.mget(loadedFromCache.ids);
if (loadedFromCache.ids.every((id) => !!cachedObjects[id] && cachedObjects[id].ts <= loadedFromCache.ts)) {
return {
items: loadedFromCache.ids.map((id) => cachedObjects[id].dt),
meta: loadedFromCache.meta,
};
}
}
else {
// return cached list if all could be loaded (from cache or with loader)
const items = yield this.getFewAsArray(loadedFromCache.ids);
if (items.every((item) => !!item)) {
return {
items,
meta: loadedFromCache.meta,
};
}
}
}
// load list with loader
loaded = unpackListData(yield this.loadList(filter));
const few = {};
for (const item of loaded.items) {
few[this.extractId(item)] = {
ts: now,
dt: item,
};
}
yield this.objectCache.msetnx(few);
yield this.listCache.set(cacheKey, {
ids: loaded.items.map(this.extractId),
meta: loaded.meta,
ts: now,
});
if (invalidationTags) {
yield this.setInvalidators(invalidationTags, now);
}
}
// save loaded items into collection's cache
for (const item of loaded.items) {
const id = this.extractId(item);
if (!this.promiseCache[id]) {
this.promiseCache[id] = Promise.resolve(item);
}
}
return loaded;
});
}
getList(filter) {
return __awaiter(this, void 0, void 0, function* () {
return (yield this.getListWithMeta(filter)).items;
});
}
invalidateCacheTag(invalidator) {
return __awaiter(this, void 0, void 0, function* () {
if (Array.isArray(invalidator)) {
yield this.invalidatorCache.mremove(invalidator);
}
else {
yield this.invalidatorCache.remove(invalidator);
}
});
}
checkInvalidators(invalidators, stamp) {
return __awaiter(this, void 0, void 0, function* () {
if (!invalidators.length) {
return true;
}
const invalidatorValues = yield this.invalidatorCache.mget(invalidators);
return invalidators.every((invalidator) => invalidatorValues[invalidator] && invalidatorValues[invalidator] <= stamp);
});
}
setInvalidators(invalidators, now) {
return __awaiter(this, void 0, void 0, function* () {
if (invalidators.length) {
const set = {};
for (const invalidator of invalidators) {
set[invalidator] = now;
}
yield this.invalidatorCache.msetnx(set);
}
});
}
}
exports.ListableCollection = ListableCollection;
//# sourceMappingURL=listable-collection.js.map