UNPKG

@grouparoo/core

Version:
135 lines (134 loc) 4.58 kB
"use strict"; 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;