@grouparoo/core
Version:
The Grouparoo Core
135 lines (134 loc) • 4.58 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.objectCache = exports.objectCacheInvalidate = exports.makeBaseCacheKey = void 0;
const actionhero_1 = require("actionhero");
const locks_1 = require("./locks");
const crypto_1 = __importDefault(require("crypto"));
async function getCacheValue({ valueKey, read, }) {
if (!read) {
return { cached: false, value: undefined };
}
try {
const cached = await actionhero_1.cache.load(valueKey);
return { cached: true, value: cached.value };
}
catch (error) {
const message = ((error === null || error === void 0 ? void 0 : error.message) || "").toString();
if (message === "Object not found" || message === "Object expired") {
return { cached: false, value: undefined };
}
throw error;
}
}
function makeObjectKey({ objectId }) {
objectId = (objectId || "").toString().trim();
if (!objectId) {
throw new Error(`objectId required`);
}
return `objectcache:${objectId}`;
}
function makeCacheString(cacheKey) {
if (cacheKey === null || cacheKey === undefined) {
return "";
}
if (typeof cacheKey === "object") {
const type = cacheKey.constructor.name;
if (type === "Date") {
const date = cacheKey;
return date.toISOString();
}
else if (type === "Array") {
const array = cacheKey;
let buffer = "";
for (let i = 0; i < array.length; i++) {
const value = makeCacheString(array[i]);
buffer += `|${i}:${value}`;
}
return buffer;
}
else if (type === "Object") {
const keys = Object.keys(cacheKey);
let buffer = "";
for (const key of keys) {
//@ts-ignore
const value = makeCacheString(cacheKey[key]);
buffer += `/${key}:${value}`;
}
return buffer;
}
}
return cacheKey.toString();
}
function makeBaseCacheKey({ objectId, cacheKey, }) {
const objectKey = makeObjectKey({ objectId });
const data = makeCacheString(cacheKey);
if (!data) {
throw new Error(`cacheKey required`);
}
const calculatedKey = crypto_1.default.createHash("md5").update(data).digest("hex");
return `${objectKey}:${calculatedKey}`;
}
exports.makeBaseCacheKey = makeBaseCacheKey;
function useRedis() {
var _a;
const running = !!((_a = actionhero_1.api === null || actionhero_1.api === void 0 ? void 0 : actionhero_1.api.redis) === null || _a === void 0 ? void 0 : _a.clients);
if (!running && actionhero_1.env === "test")
return false;
return true;
}
const objectCacheInvalidate = async ({ objectId, }) => {
if (!useRedis()) {
return;
}
const client = actionhero_1.cache.client();
const objectKey = makeObjectKey({ objectId });
const prefix = `${actionhero_1.cache.redisPrefix}${objectKey}`;
const keys = await client.keys(prefix + "*");
const jobs = [];
keys.forEach((key) => {
jobs.push(client.del(key));
});
await Promise.all(jobs);
};
exports.objectCacheInvalidate = objectCacheInvalidate;
const objectCache = async ({ objectId, cacheKey, cacheDurationMs = 1000 * 60 * 1, lock = true, read = true, write = true, passthru = false, }, methodToGetValue) => {
if (!useRedis()) {
passthru = true;
}
if (passthru) {
read = false;
write = false;
lock = false;
}
const baseKey = makeBaseCacheKey({ objectId, cacheKey });
const valueKey = `${baseKey}:value`;
const lockKey = `${baseKey}:lock`;
let { value, cached } = await getCacheValue({ valueKey, read });
if (cached) {
return value;
}
let releaseLock = null;
try {
if (lock) {
releaseLock = (await (0, locks_1.waitForLock)(lockKey)).releaseLock;
let { value, cached } = await getCacheValue({ valueKey, read });
if (cached) {
return value;
}
}
const value = await methodToGetValue();
if (write) {
await actionhero_1.cache.save(valueKey, value, cacheDurationMs);
}
return value;
}
finally {
if (releaseLock) {
await releaseLock();
}
}
};
exports.objectCache = objectCache;