import * as plugins from './levelcache.plugins.js'; import { CacheDiskManager } from './levelcache.classes.cache.diskmanager.js'; import { CacheMemoryManager } from './levelcache.classes.cache.memorymanager.js'; import { CacheS3Manager } from './levelcache.classes.cache.s3manager.js'; import { CacheEntry } from './levelcache.classes.cacheentry.js'; import { CacheRouter } from './levelcache.classes.cacherouter.js'; import { AbstractCache } from './levelcache.abstract.classes.cache.js'; export interface ILevelCacheConstructorOptions { /** * a unique id when having more than one cache */ cacheId: string; maxMemoryStorageInMB?: number; maxDiskStorageInMB?: number; maxS3StorageInMB?: number; diskStoragePath?: string; s3Config?: plugins.tsclass.storage.IS3Descriptor; s3BucketName?: string; forceLevel?: 'memory' | 'disk' | 's3'; expirationInMs?: number; immutableCache?: boolean; persistentCache?: boolean; } /** * a leveled cache for storing things for a short time */ export class LevelCache extends AbstractCache { private readyDeferred = plugins.smartpromise.defer(); public ready = this.readyDeferred.promise; public status: 'active' = 'active'; // artifact of AbstractCache public cacheRouter = new CacheRouter(this); public cacheDiskManager: CacheDiskManager; public cacheMemoryManager: CacheMemoryManager; public cacheS3Manager: CacheS3Manager; public options: ILevelCacheConstructorOptions; constructor(optionsArg: ILevelCacheConstructorOptions) { super(); this.options = { maxMemoryStorageInMB: 0.5, maxDiskStorageInMB: 10, maxS3StorageInMB: 50, ...optionsArg, }; this.init(); } public async init() { this.cacheMemoryManager = new CacheMemoryManager(this); this.cacheDiskManager = new CacheDiskManager(this); this.cacheS3Manager = new CacheS3Manager(this); await Promise.all([ this.cacheDiskManager.ready, this.cacheMemoryManager.ready, this.cacheS3Manager.ready, ]); this.readyDeferred.resolve(); } // store things public async storeCacheEntryByKey(keyArg: string, cacheEntryArg: CacheEntry): Promise { cacheEntryArg.key = keyArg; const targetCache = await this.cacheRouter.getCacheForStoreAction(keyArg, cacheEntryArg); cacheEntryArg.createdAt = Date.now(); await targetCache.storeCacheEntryByKey(keyArg, cacheEntryArg); } // retrieve things /** * retrieve cache entry */ public async retrieveCacheEntryByKey(keyArg: string): Promise { const targetCache = await this.cacheRouter.getCacheForRetrieveAction(keyArg); if (targetCache) { const cacheEntry = await targetCache.retrieveCacheEntryByKey(keyArg); if (cacheEntry.createdAt + cacheEntry.ttl < Date.now()) { await this.deleteCacheEntryByKey(keyArg).catch(); return null; } return cacheEntry; } else { return null; } } public async checkKeyPresence(keyArg: string): Promise { return plugins.smartpromise.getFirstTrueOrFalse([ this.cacheMemoryManager.checkKeyPresence(keyArg), this.cacheDiskManager.checkKeyPresence(keyArg), this.cacheS3Manager.checkKeyPresence(keyArg), ]); } public async deleteCacheEntryByKey(keyArg) { await Promise.all([ this.cacheMemoryManager.deleteCacheEntryByKey(keyArg), this.cacheDiskManager.deleteCacheEntryByKey(keyArg), this.cacheS3Manager.deleteCacheEntryByKey(keyArg), ]); } // cache maintenance public async cleanOutdated() {} /** * cleans the complete cache */ public async cleanAll(): Promise { await Promise.all([ this.cacheDiskManager.cleanAll(), this.cacheDiskManager.cleanAll(), this.cacheS3Manager.cleanAll(), ]); } }