UNPKG

mastercache

Version:

Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers

84 lines (71 loc) 2.6 kB
import pTimeout from 'p-timeout'; import type { MutexInterface } from 'async-mutex'; import type { Locks } from './locks'; import * as exceptions from '../errors'; import { events } from '../events/index'; import type { CacheStack } from './stack/cache-stack'; import type { GetSetFactory } from '../types/helpers'; import type { CacheStackWriter } from './stack/cache-stack-writer'; import type { CacheEntryOptions } from './cache-entry/cache-entry-options'; /** * Factory Runner is responsible for executing factories */ export class FactoryRunner { #stack: CacheStack; #stackWriter: CacheStackWriter; #locks: Locks; constructor(stack: CacheStack, stackWriter: CacheStackWriter, locks: Locks) { this.#stack = stack; this.#stackWriter = stackWriter; this.#locks = locks; } async saveBackgroundFactoryResult( key: string, factoryResult: unknown, options: CacheEntryOptions, lockReleaser: MutexInterface.Releaser, ) { await this.#stackWriter.set(key, factoryResult, options); this.#locks.release(key, lockReleaser); } async writeFactoryResult( key: string, item: unknown, options: CacheEntryOptions, lockReleaser: MutexInterface.Releaser, ) { await this.#stackWriter.set(key, item, options); this.#stack.emit(new events.CacheMiss(key, this.#stack.name)); this.#stack.logger.trace({ key, cache: this.#stack.name, opId: options.id }, 'cache miss'); this.#locks.release(key, lockReleaser); } async run( key: string, factory: GetSetFactory, hasFallback: boolean, options: CacheEntryOptions, lockReleaser: MutexInterface.Releaser, ) { const timeoutDuration = options.factoryTimeout(hasFallback); const timeoutException = timeoutDuration === options.timeouts?.hard ? exceptions.E_FACTORY_HARD_TIMEOUT : exceptions.E_FACTORY_SOFT_TIMEOUT; const promisifiedFactory = async () => { return await factory({ setTtl: (ttl) => options.setLogicalTtl(ttl) }); }; const factoryPromise = promisifiedFactory(); const factoryResult = await pTimeout(factoryPromise, { milliseconds: timeoutDuration ?? Number.POSITIVE_INFINITY, fallback: async () => { factoryPromise .then((result) => this.saveBackgroundFactoryResult(key, result, options, lockReleaser)) .catch(() => {}) .finally(() => this.#locks.release(key, lockReleaser)); throw new timeoutException(); }, }); await this.writeFactoryResult(key, factoryResult, options, lockReleaser); return factoryResult; } }