@midwayjs/cache-manager
Version:
midway cache manager
197 lines (196 loc) • 7.67 kB
JavaScript
/**
* fork from https://github.com/node-cache-manager/node-cache-manager/tree/master
* reason: Support node.js v18 version below and add some features
* for example: add methodWrap
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.multiCaching = exports.createCache = exports.caching = void 0;
const prmoiseCoalesce_1 = require("./prmoiseCoalesce");
const store_1 = require("./store");
/**
* Generic caching interface that wraps any caching library with a compatible interface.
*/
async function caching(factory, args) {
if (factory === 'memory') {
const store = (0, store_1.memoryStore)(args);
return createCache(store, args);
}
if (typeof factory === 'function') {
const store = await factory(args);
return createCache(store, args);
}
return createCache(factory, args);
}
exports.caching = caching;
/**
* Create cache instance by store (non-async).
*/
function createCache(store, args) {
return {
/**
* Wraps a function in cache. I.e., the first time the function is run,
* its results are stored in cache so subsequent calls retrieve from cache
* instead of calling the function.
* @example
* const result = await cache.wrap('key', () => Promise.resolve(1));
*
*/
wrap: async (key, fn, ttl) => {
return (0, prmoiseCoalesce_1.coalesceAsync)(key, async () => {
const value = await store.get(key);
if (value === undefined) {
const result = await fn();
const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl;
await store.set(key, result, cacheTTL);
return result;
}
else if (args === null || args === void 0 ? void 0 : args.refreshThreshold) {
const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl;
const remainingTtl = await store.ttl(key);
if (remainingTtl !== -1 && remainingTtl < args.refreshThreshold) {
(0, prmoiseCoalesce_1.coalesceAsync)(`+++${key}`, fn).then(result => store.set(key, result, cacheTTL));
}
}
return value;
});
},
store: store,
del: (key) => store.del(key),
get: (key) => store.get(key),
set: (key, value, ttl) => store.set(key, value, ttl),
reset: () => store.reset(),
/**
* cache function with args
*/
methodWrap: async (key, fn, fnArgs, ttl) => {
return (0, prmoiseCoalesce_1.coalesceAsync)(key, async () => {
const value = await store.get(key);
if (value === undefined) {
const result = await fn(...fnArgs);
const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl;
await store.set(key, result, cacheTTL);
return result;
}
else if (args === null || args === void 0 ? void 0 : args.refreshThreshold) {
const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl;
const remainingTtl = await store.ttl(key);
if (remainingTtl !== -1 && remainingTtl < args.refreshThreshold) {
// fn(...fnArgs).then(result => store.set(key, result, cacheTTL));
(0, prmoiseCoalesce_1.coalesceAsync)(`+++${key}`, () => fn(...fnArgs)).then(result => store.set(key, result, cacheTTL));
}
}
return value;
});
},
};
}
exports.createCache = createCache;
/**
* Module that lets you specify a hierarchy of caches.
*/
function multiCaching(caches) {
const get = async (key) => {
for (const cache of caches) {
try {
const val = await cache.get(key);
if (val !== undefined)
return val;
}
catch (e) {
// ignore
}
}
};
const set = async (key, data, ttl) => {
await Promise.all(caches.map(cache => cache.set(key, data, ttl)));
};
return {
get,
set,
del: async (key) => {
await Promise.all(caches.map(cache => cache.del(key)));
},
async wrap(key, fn, ttl) {
let value;
let i = 0;
for (; i < caches.length; i++) {
try {
value = await caches[i].get(key);
if (value !== undefined)
break;
}
catch (e) {
// ignore
}
}
if (value === undefined) {
const result = await fn();
const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl;
await set(key, result, cacheTTL);
return result;
}
else {
const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl;
Promise.all(caches.slice(0, i).map(cache => cache.set(key, value, cacheTTL))).then();
caches[i].wrap(key, fn, ttl).then(); // call wrap for store for internal refreshThreshold logic, see: src/caching.ts caching.wrap
}
return value;
},
reset: async () => {
await Promise.all(caches.map(x => x.reset()));
},
mget: async (...keys) => {
const values = new Array(keys.length).fill(undefined);
for (const cache of caches) {
if (values.every(x => x !== undefined))
break;
try {
const val = await cache.store.mget(...keys);
val.forEach((v, i) => {
if (values[i] === undefined && v !== undefined)
values[i] = v;
});
}
catch (e) {
// ignore
}
}
return values;
},
mset: async (args, ttl) => {
await Promise.all(caches.map(cache => cache.store.mset(args, ttl)));
},
mdel: async (...keys) => {
await Promise.all(caches.map(cache => cache.store.mdel(...keys)));
},
methodWrap: async (key, fn, fnArgs, ttl) => {
let value;
let i = 0;
for (; i < caches.length; i++) {
try {
value = await caches[i].get(key);
if (value !== undefined)
break;
}
catch (e) {
// ignore
}
}
if (value === undefined) {
const result = await fn(...fnArgs);
const cacheTTL = typeof ttl === 'function' ? ttl(result) : ttl;
await set(key, result, cacheTTL);
return result;
}
else {
const cacheTTL = typeof ttl === 'function' ? ttl(value) : ttl;
Promise.all(caches.slice(0, i).map(cache => cache.set(key, value, cacheTTL))).then();
caches[i].methodWrap(key, fn, fnArgs, ttl).then(); // call wrap for store for internal refreshThreshold logic, see: src/caching.ts caching.wrap
}
return value;
},
};
}
exports.multiCaching = multiCaching;
//# sourceMappingURL=cacheManager.js.map
;