@credo-ts/core
Version:
<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1
1 lines • 7.9 kB
Source Map (JSON)
{"version":3,"file":"SingleContextStorageLruCache.mjs","names":[],"sources":["../../../../src/modules/cache/singleContextLruCache/SingleContextStorageLruCache.ts"],"sourcesContent":["import LRUMap from 'lru_map'\nimport type { AgentContext } from '../../../agent/context'\nimport { CredoError, RecordDuplicateError } from '../../../error'\nimport type { Cache } from '../Cache'\nimport type { SingleContextLruCacheItem } from './SingleContextLruCacheRecord'\n\nimport { SingleContextLruCacheRecord } from './SingleContextLruCacheRecord'\nimport { SingleContextLruCacheRepository } from './SingleContextLruCacheRepository'\n\nconst CONTEXT_STORAGE_LRU_CACHE_ID = 'CONTEXT_STORAGE_LRU_CACHE_ID'\n\nexport interface SingleContextStorageLruCacheOptions {\n /** The maximum number of entries allowed in the cache */\n limit: number\n}\n\n/**\n * Cache that leverages the storage associated with the agent context to store cache records.\n * It will keep an in-memory cache of the records to avoid hitting the storage on every read request.\n * Therefor this cache is meant to be used with a single instance of the agent.\n *\n * Due to keeping an in-memory copy of the cache, it is also not meant to be used with multiple\n * agent context instances (meaning multi-tenancy), as they will overwrite the in-memory cache.\n *\n * However, this means the cache is not meant for usage with multiple instances.\n */\nexport class SingleContextStorageLruCache implements Cache {\n private limit: number\n private _cache?: LRUMap.LRUMap<string, SingleContextLruCacheItem>\n private _contextCorrelationId?: string\n\n public constructor({ limit }: SingleContextStorageLruCacheOptions) {\n this.limit = limit\n }\n\n public async get<CacheValue>(agentContext: AgentContext, key: string) {\n this.assertContextCorrelationId(agentContext)\n\n const cache = await this.getCache(agentContext)\n this.removeExpiredItems(cache)\n\n const item = cache.get(key)\n\n // Does not exist\n if (!item) return null\n\n // Expired\n if (item.expiresAt && Date.now() > item.expiresAt) {\n cache.delete(key)\n await this.persistCache(agentContext)\n return null\n }\n\n return item.value as CacheValue\n }\n\n public async set<CacheValue>(\n agentContext: AgentContext,\n key: string,\n value: CacheValue,\n expiresInSeconds?: number\n ): Promise<void> {\n this.assertContextCorrelationId(agentContext)\n\n let expiresDate: Date | undefined\n\n if (expiresInSeconds) {\n expiresDate = new Date()\n expiresDate.setSeconds(expiresDate.getSeconds() + expiresInSeconds)\n }\n\n const cache = await this.getCache(agentContext)\n this.removeExpiredItems(cache)\n\n cache.set(key, {\n expiresAt: expiresDate?.getTime(),\n value,\n })\n await this.persistCache(agentContext)\n }\n\n public async remove(agentContext: AgentContext, key: string): Promise<void> {\n this.assertContextCorrelationId(agentContext)\n\n const cache = await this.getCache(agentContext)\n this.removeExpiredItems(cache)\n cache.delete(key)\n\n await this.persistCache(agentContext)\n }\n\n private async getCache(agentContext: AgentContext) {\n if (!this._cache) {\n const cacheRecord = await this.fetchCacheRecord(agentContext)\n this._cache = this.lruFromRecord(cacheRecord)\n }\n\n return this._cache\n }\n\n private lruFromRecord(cacheRecord: SingleContextLruCacheRecord) {\n return new LRUMap.LRUMap<string, SingleContextLruCacheItem>(this.limit, cacheRecord.entries.entries())\n }\n\n private async fetchCacheRecord(agentContext: AgentContext) {\n const cacheRepository = agentContext.dependencyManager.resolve(SingleContextLruCacheRepository)\n let cacheRecord = await cacheRepository.findById(agentContext, CONTEXT_STORAGE_LRU_CACHE_ID)\n\n if (!cacheRecord) {\n cacheRecord = new SingleContextLruCacheRecord({\n id: CONTEXT_STORAGE_LRU_CACHE_ID,\n entries: new Map(),\n })\n\n try {\n await cacheRepository.save(agentContext, cacheRecord)\n } catch (error) {\n // This addresses some race conditions issues where we first check if the record exists\n // then we create one if it doesn't, but another process has created one in the meantime\n // Although not the most elegant solution, it addresses the issues\n if (error instanceof RecordDuplicateError) {\n // the record already exists, which is our intended end state\n // we can ignore this error and fetch the existing record\n return cacheRepository.getById(agentContext, CONTEXT_STORAGE_LRU_CACHE_ID)\n }\n throw error\n }\n }\n\n return cacheRecord\n }\n\n private removeExpiredItems(cache: LRUMap.LRUMap<string, SingleContextLruCacheItem>) {\n cache.forEach((value, key) => {\n if (value.expiresAt && Date.now() > value.expiresAt) {\n cache.delete(key)\n }\n })\n }\n\n private async persistCache(agentContext: AgentContext) {\n const cacheRepository = agentContext.dependencyManager.resolve(SingleContextLruCacheRepository)\n const cache = await this.getCache(agentContext)\n\n await cacheRepository.update(\n agentContext,\n new SingleContextLruCacheRecord({\n entries: new Map(cache.toJSON().map(({ key, value }) => [key, value])),\n id: CONTEXT_STORAGE_LRU_CACHE_ID,\n })\n )\n }\n\n /**\n * Asserts this class is not used with multiple agent context instances.\n */\n private assertContextCorrelationId(agentContext: AgentContext) {\n if (!this._contextCorrelationId) {\n this._contextCorrelationId = agentContext.contextCorrelationId\n }\n\n if (this._contextCorrelationId !== agentContext.contextCorrelationId) {\n throw new CredoError(\n 'SingleContextStorageLruCache can not be used with multiple agent context instances. Register a custom cache implementation in the CacheModule.'\n )\n }\n }\n}\n"],"mappings":";;;;;;;;;;AASA,MAAM,+BAA+B;;;;;;;;;;;AAiBrC,IAAa,+BAAb,MAA2D;CAKzD,AAAO,YAAY,EAAE,SAA8C;AACjE,OAAK,QAAQ;;CAGf,MAAa,IAAgB,cAA4B,KAAa;AACpE,OAAK,2BAA2B,aAAa;EAE7C,MAAM,QAAQ,MAAM,KAAK,SAAS,aAAa;AAC/C,OAAK,mBAAmB,MAAM;EAE9B,MAAM,OAAO,MAAM,IAAI,IAAI;AAG3B,MAAI,CAAC,KAAM,QAAO;AAGlB,MAAI,KAAK,aAAa,KAAK,KAAK,GAAG,KAAK,WAAW;AACjD,SAAM,OAAO,IAAI;AACjB,SAAM,KAAK,aAAa,aAAa;AACrC,UAAO;;AAGT,SAAO,KAAK;;CAGd,MAAa,IACX,cACA,KACA,OACA,kBACe;AACf,OAAK,2BAA2B,aAAa;EAE7C,IAAI;AAEJ,MAAI,kBAAkB;AACpB,iCAAc,IAAI,MAAM;AACxB,eAAY,WAAW,YAAY,YAAY,GAAG,iBAAiB;;EAGrE,MAAM,QAAQ,MAAM,KAAK,SAAS,aAAa;AAC/C,OAAK,mBAAmB,MAAM;AAE9B,QAAM,IAAI,KAAK;GACb,WAAW,aAAa,SAAS;GACjC;GACD,CAAC;AACF,QAAM,KAAK,aAAa,aAAa;;CAGvC,MAAa,OAAO,cAA4B,KAA4B;AAC1E,OAAK,2BAA2B,aAAa;EAE7C,MAAM,QAAQ,MAAM,KAAK,SAAS,aAAa;AAC/C,OAAK,mBAAmB,MAAM;AAC9B,QAAM,OAAO,IAAI;AAEjB,QAAM,KAAK,aAAa,aAAa;;CAGvC,MAAc,SAAS,cAA4B;AACjD,MAAI,CAAC,KAAK,QAAQ;GAChB,MAAM,cAAc,MAAM,KAAK,iBAAiB,aAAa;AAC7D,QAAK,SAAS,KAAK,cAAc,YAAY;;AAG/C,SAAO,KAAK;;CAGd,AAAQ,cAAc,aAA0C;AAC9D,SAAO,IAAI,OAAO,OAA0C,KAAK,OAAO,YAAY,QAAQ,SAAS,CAAC;;CAGxG,MAAc,iBAAiB,cAA4B;EACzD,MAAM,kBAAkB,aAAa,kBAAkB,QAAQ,gCAAgC;EAC/F,IAAI,cAAc,MAAM,gBAAgB,SAAS,cAAc,6BAA6B;AAE5F,MAAI,CAAC,aAAa;AAChB,iBAAc,IAAI,4BAA4B;IAC5C,IAAI;IACJ,yBAAS,IAAI,KAAK;IACnB,CAAC;AAEF,OAAI;AACF,UAAM,gBAAgB,KAAK,cAAc,YAAY;YAC9C,OAAO;AAId,QAAI,iBAAiB,qBAGnB,QAAO,gBAAgB,QAAQ,cAAc,6BAA6B;AAE5E,UAAM;;;AAIV,SAAO;;CAGT,AAAQ,mBAAmB,OAAyD;AAClF,QAAM,SAAS,OAAO,QAAQ;AAC5B,OAAI,MAAM,aAAa,KAAK,KAAK,GAAG,MAAM,UACxC,OAAM,OAAO,IAAI;IAEnB;;CAGJ,MAAc,aAAa,cAA4B;EACrD,MAAM,kBAAkB,aAAa,kBAAkB,QAAQ,gCAAgC;EAC/F,MAAM,QAAQ,MAAM,KAAK,SAAS,aAAa;AAE/C,QAAM,gBAAgB,OACpB,cACA,IAAI,4BAA4B;GAC9B,SAAS,IAAI,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,KAAK,YAAY,CAAC,KAAK,MAAM,CAAC,CAAC;GACtE,IAAI;GACL,CAAC,CACH;;;;;CAMH,AAAQ,2BAA2B,cAA4B;AAC7D,MAAI,CAAC,KAAK,sBACR,MAAK,wBAAwB,aAAa;AAG5C,MAAI,KAAK,0BAA0B,aAAa,qBAC9C,OAAM,IAAI,WACR,iJACD"}