@autofleet/matmon 3.0.13 → 3.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Mutex","LRU","valueToReturn: V | null","env","lock: Awaited<ReturnType<typeof this.lock>> | undefined"],"sources":["../src/cache.ts","../src/redis/index.ts"],"sourcesContent":["import LRU from 'lru-cache';\nimport { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst MAX_SIZE = 100_000;\nconst DEFAULT_CACHE_SIZE = 300;\nconst CACHE_LOCK_TIMEOUT_MILIS = 3_000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface & { [Symbol.dispose]: () => void; } => {\n MUTEX_MAP[key] ??= withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n const mutex = MUTEX_MAP[key];\n return Object.assign(mutex, {\n [Symbol.dispose]: () => {\n mutex.release();\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n },\n });\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU<K, V>(getOptions<K, V>({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;\n cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n logger: LoggerInstanceManager;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>, logger: LoggerInstanceManager): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n logger,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, res), logger);\n return res;\n }\n let valueToReturn: V | null = null;\n using mutex = getMutexByCacheKey(cacheKey);\n try {\n await mutex.acquire();\n valueToReturn = await cacheGet(cacheKey);\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n } else {\n // logger.info('get value from cache');\n }\n } catch {\n // retry without locking if failed\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n }\n return valueToReturn;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n logger: LoggerInstanceManager;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n logger,\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject), logger);\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value), logger);\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { env } from 'node:process';\nimport { createClient } from 'redis';\nimport redisLock from 'redis-lock';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = Number.parseInt(env.REDIS_HOST_PORT || '', 10) || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5_000;\nconst DEFAULT_LOCK_DURATION = 1_000;\nconst DEFAULT_BASE_TTL = 3_600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */\n lockTimeout?: number;\n /** The time that a key will be locked. */\n lockDuration?: number;\n /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */\n ttl?: number;\n /** @default false */\n useLock?: boolean;\n logger: LoggerInstanceManager;\n /** Redis client to allow reusing connections. */\n client?: ReturnType<typeof createClient>;\n}\n\nclass RedisCache {\n private readonly client: ReturnType<typeof createClient>;\n private readonly locker: ReturnType<typeof redisLock>;\n private readonly lockTimeout: number;\n private readonly lockDuration: number;\n private readonly logger: LoggerInstanceManager;\n\n // A dictionary of key and its lock.\n locks: Record<string, Awaited<ReturnType<typeof this.lock>> | undefined> = {};\n\n private readonly baseTTL: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.logger = options.logger;\n this.client = options.client ?? createClient({\n socket: {\n host: options.host || HOST,\n port: options.port || PORT,\n },\n }).on('error', (err) => {\n this.logger.error('matmon: Redis error', { err });\n });\n if (!options.client) {\n this.client.connect().catch((err) => {\n this.logger.error('matmon: Failed to connect to Redis', { err });\n });\n }\n this.locker = redisLock(this.client, options.lockRetries ?? 10);\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n public readonly get = async <T = any>(key: string): Promise<T> => {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.get(keyWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n\n if (this.useLock) {\n let lock: Awaited<ReturnType<typeof this.lock>> | undefined;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to lock key (${keyWithPrefix})`, { cause: err });\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n };\n\n public readonly getMultiple = async <T = any>(keys: string[]): Promise<T[]> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mGet(keysWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n return values.map(value => JSON.parse(value));\n };\n\n public readonly set = async <T = any>(key: string, value: T): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL + (Math.random() + 1)), 10);\n try {\n await this.client.set(keyWithPrefix, JSON.stringify(value), { EX: ttl });\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n throw new Error('Failed to set a key-value pair', { cause: err });\n }\n };\n\n public readonly setMultiple = async <T = any>(keyValues: Record<string, T>): Promise<void> => {\n if (typeof keyValues !== 'object') {\n const error = new Error('keyValues must be an object');\n this.logger.error('keyValues must be an object', { error });\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map<[string, string]>(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.mSet(keyValuesWithPrefix);\n keyValuesWithPrefix.map(([key]) => multi.expire(key, ttl));\n await multi.exec();\n // @ts-expect-error we are returning a value although the type definition says void\n return setPromise;\n } catch (err) {\n throw new Error('Failed to set multiple key-value pairs', { cause: err });\n }\n };\n\n public readonly remove = async (key: string): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.del(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete key ${keyWithPrefix}`, { cause: err });\n }\n };\n\n public readonly removeMultiple = async (keys: string[]): Promise<void> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.del(keysWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, { cause: err });\n }\n };\n\n public readonly getClient = (): typeof this.client => this.client;\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["Mutex","LRU","valueToReturn: V | null","env","lock: Awaited<ReturnType<typeof this.lock>> | undefined"],"sources":["../src/cache.ts","../src/redis/index.ts"],"sourcesContent":["import LRU from 'lru-cache';\nimport { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst MAX_SIZE = 100_000;\nconst DEFAULT_CACHE_SIZE = 300;\nconst CACHE_LOCK_TIMEOUT_MILIS = 3_000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface & { [Symbol.dispose]: () => void; } => {\n MUTEX_MAP[key] ??= withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n const mutex = MUTEX_MAP[key];\n return Object.assign(mutex, {\n [Symbol.dispose]: () => {\n mutex.release();\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n },\n });\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU(getOptions({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;\n cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n logger: LoggerInstanceManager;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>, logger: LoggerInstanceManager): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n logger,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, res), logger);\n return res;\n }\n let valueToReturn: V | null = null;\n using mutex = getMutexByCacheKey(cacheKey);\n try {\n await mutex.acquire();\n valueToReturn = await cacheGet(cacheKey);\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n } else {\n // logger.info('get value from cache');\n }\n } catch {\n // retry without locking if failed\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n }\n return valueToReturn;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n logger: LoggerInstanceManager;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n logger,\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject), logger);\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value), logger);\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { env } from 'node:process';\nimport { createClient } from 'redis';\nimport redisLock from 'redis-lock';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = Number.parseInt(env.REDIS_HOST_PORT || '', 10) || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5_000;\nconst DEFAULT_LOCK_DURATION = 1_000;\nconst DEFAULT_BASE_TTL = 3_600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */\n lockTimeout?: number;\n /** The time that a key will be locked. */\n lockDuration?: number;\n /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */\n ttl?: number;\n /** @default false */\n useLock?: boolean;\n logger: LoggerInstanceManager;\n /** Redis client to allow reusing connections. */\n client?: ReturnType<typeof createClient>;\n}\n\nclass RedisCache {\n private readonly client: ReturnType<typeof createClient>;\n private readonly locker: ReturnType<typeof redisLock>;\n private readonly lockTimeout: number;\n private readonly lockDuration: number;\n private readonly logger: LoggerInstanceManager;\n\n // A dictionary of key and its lock.\n locks: Record<string, Awaited<ReturnType<typeof this.lock>> | undefined> = {};\n\n private readonly baseTTL: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.logger = options.logger;\n this.client = options.client ?? createClient({\n socket: {\n host: options.host || HOST,\n port: options.port || PORT,\n },\n }).on('error', (err) => {\n this.logger.error('matmon: Redis error', { err });\n });\n if (!options.client) {\n this.client.connect().catch((err) => {\n this.logger.error('matmon: Failed to connect to Redis', { err });\n });\n }\n this.locker = redisLock(this.client, options.lockRetries ?? 10);\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n public readonly get = async <T = any>(key: string): Promise<T> => {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.get(keyWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n\n if (this.useLock) {\n let lock: Awaited<ReturnType<typeof this.lock>> | undefined;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to lock key (${keyWithPrefix})`, { cause: err });\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n };\n\n public readonly getMultiple = async <T = any>(keys: string[]): Promise<T[]> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mGet(keysWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n return values.map(value => JSON.parse(value));\n };\n\n public readonly set = async <T = any>(key: string, value: T): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL + (Math.random() + 1)), 10);\n try {\n await this.client.set(keyWithPrefix, JSON.stringify(value), { EX: ttl });\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n throw new Error('Failed to set a key-value pair', { cause: err });\n }\n };\n\n public readonly setMultiple = async <T = any>(keyValues: Record<string, T>): Promise<void> => {\n if (typeof keyValues !== 'object') {\n const error = new Error('keyValues must be an object');\n this.logger.error('keyValues must be an object', { error });\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map<[string, string]>(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.mSet(keyValuesWithPrefix);\n keyValuesWithPrefix.map(([key]) => multi.expire(key, ttl));\n await multi.exec();\n // @ts-expect-error we are returning a value although the type definition says void\n return setPromise;\n } catch (err) {\n throw new Error('Failed to set multiple key-value pairs', { cause: err });\n }\n };\n\n public readonly remove = async (key: string): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.del(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete key ${keyWithPrefix}`, { cause: err });\n }\n };\n\n public readonly removeMultiple = async (keys: string[]): Promise<void> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.del(keysWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, { cause: err });\n }\n };\n\n public readonly getClient = (): typeof this.client => this.client;\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n"],"mappings":"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"}
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["valueToReturn: V | null","lock: Awaited<ReturnType<typeof this.lock>> | undefined"],"sources":["../src/cache.ts","../src/redis/index.ts"],"sourcesContent":["import LRU from 'lru-cache';\nimport { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst MAX_SIZE = 100_000;\nconst DEFAULT_CACHE_SIZE = 300;\nconst CACHE_LOCK_TIMEOUT_MILIS = 3_000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface & { [Symbol.dispose]: () => void; } => {\n MUTEX_MAP[key] ??= withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n const mutex = MUTEX_MAP[key];\n return Object.assign(mutex, {\n [Symbol.dispose]: () => {\n mutex.release();\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n },\n });\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU<K, V>(getOptions<K, V>({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;\n cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n logger: LoggerInstanceManager;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>, logger: LoggerInstanceManager): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n logger,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, res), logger);\n return res;\n }\n let valueToReturn: V | null = null;\n using mutex = getMutexByCacheKey(cacheKey);\n try {\n await mutex.acquire();\n valueToReturn = await cacheGet(cacheKey);\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n } else {\n // logger.info('get value from cache');\n }\n } catch {\n // retry without locking if failed\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n }\n return valueToReturn;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n logger: LoggerInstanceManager;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n logger,\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject), logger);\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value), logger);\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { env } from 'node:process';\nimport { createClient } from 'redis';\nimport redisLock from 'redis-lock';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = Number.parseInt(env.REDIS_HOST_PORT || '', 10) || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5_000;\nconst DEFAULT_LOCK_DURATION = 1_000;\nconst DEFAULT_BASE_TTL = 3_600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */\n lockTimeout?: number;\n /** The time that a key will be locked. */\n lockDuration?: number;\n /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */\n ttl?: number;\n /** @default false */\n useLock?: boolean;\n logger: LoggerInstanceManager;\n /** Redis client to allow reusing connections. */\n client?: ReturnType<typeof createClient>;\n}\n\nclass RedisCache {\n private readonly client: ReturnType<typeof createClient>;\n private readonly locker: ReturnType<typeof redisLock>;\n private readonly lockTimeout: number;\n private readonly lockDuration: number;\n private readonly logger: LoggerInstanceManager;\n\n // A dictionary of key and its lock.\n locks: Record<string, Awaited<ReturnType<typeof this.lock>> | undefined> = {};\n\n private readonly baseTTL: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.logger = options.logger;\n this.client = options.client ?? createClient({\n socket: {\n host: options.host || HOST,\n port: options.port || PORT,\n },\n }).on('error', (err) => {\n this.logger.error('matmon: Redis error', { err });\n });\n if (!options.client) {\n this.client.connect().catch((err) => {\n this.logger.error('matmon: Failed to connect to Redis', { err });\n });\n }\n this.locker = redisLock(this.client, options.lockRetries ?? 10);\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n public readonly get = async <T = any>(key: string): Promise<T> => {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.get(keyWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n\n if (this.useLock) {\n let lock: Awaited<ReturnType<typeof this.lock>> | undefined;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to lock key (${keyWithPrefix})`, { cause: err });\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n };\n\n public readonly getMultiple = async <T = any>(keys: string[]): Promise<T[]> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mGet(keysWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n return values.map(value => JSON.parse(value));\n };\n\n public readonly set = async <T = any>(key: string, value: T): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL + (Math.random() + 1)), 10);\n try {\n await this.client.set(keyWithPrefix, JSON.stringify(value), { EX: ttl });\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n throw new Error('Failed to set a key-value pair', { cause: err });\n }\n };\n\n public readonly setMultiple = async <T = any>(keyValues: Record<string, T>): Promise<void> => {\n if (typeof keyValues !== 'object') {\n const error = new Error('keyValues must be an object');\n this.logger.error('keyValues must be an object', { error });\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map<[string, string]>(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.mSet(keyValuesWithPrefix);\n keyValuesWithPrefix.map(([key]) => multi.expire(key, ttl));\n await multi.exec();\n // @ts-expect-error we are returning a value although the type definition says void\n return setPromise;\n } catch (err) {\n throw new Error('Failed to set multiple key-value pairs', { cause: err });\n }\n };\n\n public readonly remove = async (key: string): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.del(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete key ${keyWithPrefix}`, { cause: err });\n }\n };\n\n public readonly removeMultiple = async (keys: string[]): Promise<void> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.del(keysWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, { cause: err });\n }\n };\n\n public readonly getClient = (): typeof this.client => this.client;\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["valueToReturn: V | null","lock: Awaited<ReturnType<typeof this.lock>> | undefined"],"sources":["../src/cache.ts","../src/redis/index.ts"],"sourcesContent":["import LRU from 'lru-cache';\nimport { Mutex, type MutexInterface, withTimeout } from 'async-mutex';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst MAX_SIZE = 100_000;\nconst DEFAULT_CACHE_SIZE = 300;\nconst CACHE_LOCK_TIMEOUT_MILIS = 3_000;\nconst LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';\nconst MUTEX_MAP = {} as Record<keyof any, undefined | MutexInterface>;\n\ninterface Options {\n lifeTimeInSec: number;\n size?: number;\n}\n\nconst getOptions = <K, V>({\n lifeTimeInSec,\n size = DEFAULT_CACHE_SIZE,\n}: Options): LRU.Options<K, V> => ({\n max: Math.min(size, MAX_SIZE),\n maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,\n});\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\n\nconst getMutexByCacheKey = (key: keyof any): MutexInterface & { [Symbol.dispose]: () => void; } => {\n MUTEX_MAP[key] ??= withTimeout(new Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));\n const mutex = MUTEX_MAP[key];\n return Object.assign(mutex, {\n [Symbol.dispose]: () => {\n mutex.release();\n if (MUTEX_MAP[key]) {\n delete MUTEX_MAP[key];\n }\n },\n });\n};\n\nexport const getNewLRU = <K = unknown, V = unknown>(lifeTimeInSec: Options['lifeTimeInSec'], size?: Options['size']): LRU<K, V> => new LRU(getOptions({\n lifeTimeInSec,\n size,\n}));\n\ninterface GetWithCacheOptions<V = unknown> {\n cacheKey: string;\n cacheGet: (cacheKey: string) => Promise<NoInfer<V>>;\n cacheSet: (cacheKey: string, value: NoInfer<V>) => Promise<void>;\n fetching: () => Promise<V>;\n skipCache?: boolean;\n logger: LoggerInstanceManager;\n}\n\nconst IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';\nconst IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;\n\nconst tryToSetInCache = async (callback: () => void | PromiseLike<void>, logger: LoggerInstanceManager): Promise<void> => {\n try {\n await callback();\n } catch (e) {\n logger.error('Failed to set in cache', e);\n }\n};\n\nexport const getWithCacheSupport = async <V = unknown>({\n cacheKey,\n cacheGet,\n cacheSet,\n fetching,\n skipCache,\n logger,\n}: GetWithCacheOptions<V>): Promise<V> => {\n if (skipCache || IS_IN_SERVICE_TEST) {\n const res = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, res), logger);\n return res;\n }\n let valueToReturn: V | null = null;\n using mutex = getMutexByCacheKey(cacheKey);\n try {\n await mutex.acquire();\n valueToReturn = await cacheGet(cacheKey);\n if (!valueToReturn) {\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n } else {\n // logger.info('get value from cache');\n }\n } catch {\n // retry without locking if failed\n valueToReturn = await fetching();\n await tryToSetInCache(() => cacheSet(cacheKey, valueToReturn!), logger);\n }\n return valueToReturn;\n};\n\nconst getIdField = (query: string | object, idField: keyof any): string => {\n if (typeof query === 'string') {\n return query;\n }\n return query[idField];\n};\n\ninterface GetMultipleWithCacheOptions<V = unknown> {\n getFromCache?: (query: any) => Promise<V>;\n multiGetterFromCache?: (queries: any[]) => Promise<V[]>;\n setInCache: (key: string, value: V) => Promise<void>;\n getter?: (query: any) => Promise<V>;\n multiGetter?: (queries: any[]) => Promise<V[]>;\n setMultiInCache?: (keyValues: Record<string, object>) => Promise<void>;\n idField?: string;\n logger: LoggerInstanceManager;\n}\n\nexport const getMultipleWithCache = <V = unknown>({\n getFromCache,\n multiGetterFromCache,\n setInCache,\n setMultiInCache,\n getter,\n multiGetter,\n idField = 'id',\n logger,\n}: GetMultipleWithCacheOptions<V>): (queries: any[]) => Promise<(V | null | undefined)[]> => async (queries) => {\n const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));\n const resultMap = new Map<string, V>();\n\n const valuesToPullFromCache = [...queriesMap.values()];\n\n const valuesFromCache = await (\n multiGetterFromCache?.(valuesToPullFromCache) // Use multiGetterFromCache if it's provided\n ?? Promise.all(valuesToPullFromCache.map(query => getFromCache!(query))) // Otherwise, iterate over the queries with getFromCache\n );\n\n valuesFromCache.filter(Boolean).forEach((value) => {\n queriesMap.delete(getIdField(value as object, idField));\n resultMap.set(getIdField(value as object, idField), value);\n });\n\n if (queriesMap.size > 0) {\n const valuesFromGetter = await (\n multiGetter?.([...queriesMap.values()]) // Use multiGetter if it's provided\n ?? Promise.all([...queriesMap.values()].map(id => getter!(id))) // Otherwise, iterate over the queries with getter\n );\n\n if (setMultiInCache && valuesFromGetter.length > 0) {\n const setCacheObject = valuesFromGetter.reduce((acc, value) => {\n acc[getIdField(value as object, idField)] = value;\n return acc;\n }, {});\n await tryToSetInCache(() => setMultiInCache(setCacheObject), logger);\n }\n\n valuesFromGetter.forEach((value) => {\n if (!value) {\n return;\n }\n if (!setMultiInCache) {\n void tryToSetInCache(() => setInCache(value[idField], value), logger);\n }\n resultMap.set(getIdField(value as object, idField), value);\n });\n }\n\n return queries.map((query) => {\n if (!query) {\n return null;\n }\n return resultMap.get(getIdField(query, idField));\n });\n};\n","import { env } from 'node:process';\nimport { createClient } from 'redis';\nimport redisLock from 'redis-lock';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\n\nconst HOST = env.REDIS_HOST_NAME || '127.0.0.1';\nconst PORT = Number.parseInt(env.REDIS_HOST_PORT || '', 10) || 6379;\nconst SERVICE_NAME = env.AF_SERVICE_NAME;\nconst DEFAULT_LOCK_TIMEOUT = 5_000;\nconst DEFAULT_LOCK_DURATION = 1_000;\nconst DEFAULT_BASE_TTL = 3_600;\nconst KEY_PREFIX = SERVICE_NAME + ':';\n\nif (!SERVICE_NAME) {\n throw new Error('SERVICE_NAME cannot be null, please check your env.AF_SERVICE_NAME');\n}\n\ninterface RedisCacheOptions {\n host?: string;\n port?: number;\n lockRetries?: number;\n /** The time that the cache will wait until a lock will be considered as failed (in milliseconds). */\n lockTimeout?: number;\n /** The time that a key will be locked. */\n lockDuration?: number;\n /** The time (in seconds) that a key-value pair will exist in redis. To this base time will be added between 1 to 2 milliseconds. */\n ttl?: number;\n /** @default false */\n useLock?: boolean;\n logger: LoggerInstanceManager;\n /** Redis client to allow reusing connections. */\n client?: ReturnType<typeof createClient>;\n}\n\nclass RedisCache {\n private readonly client: ReturnType<typeof createClient>;\n private readonly locker: ReturnType<typeof redisLock>;\n private readonly lockTimeout: number;\n private readonly lockDuration: number;\n private readonly logger: LoggerInstanceManager;\n\n // A dictionary of key and its lock.\n locks: Record<string, Awaited<ReturnType<typeof this.lock>> | undefined> = {};\n\n private readonly baseTTL: number;\n\n keyPrefix: string = KEY_PREFIX;\n\n useLock: boolean;\n\n constructor(options: RedisCacheOptions) {\n this.logger = options.logger;\n this.client = options.client ?? createClient({\n socket: {\n host: options.host || HOST,\n port: options.port || PORT,\n },\n }).on('error', (err) => {\n this.logger.error('matmon: Redis error', { err });\n });\n if (!options.client) {\n this.client.connect().catch((err) => {\n this.logger.error('matmon: Failed to connect to Redis', { err });\n });\n }\n this.locker = redisLock(this.client, options.lockRetries ?? 10);\n this.lockTimeout = options.lockTimeout ?? DEFAULT_LOCK_TIMEOUT;\n this.lockDuration = options.lockDuration ?? DEFAULT_LOCK_DURATION;\n this.baseTTL = options.ttl ?? DEFAULT_BASE_TTL;\n this.useLock = !!options.useLock;\n }\n\n public readonly get = async <T = any>(key: string): Promise<T> => {\n const keyWithPrefix = KEY_PREFIX + key;\n\n let value;\n try {\n // Try to get the value from redis.\n value = await this.client.get(keyWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n\n if (this.useLock) {\n let lock: Awaited<ReturnType<typeof this.lock>> | undefined;\n try {\n // Try to lock the key.\n lock = await this.lock(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to lock key (${keyWithPrefix})`, { cause: err });\n }\n\n // If the lock did not fail, add it to a locks dictionary.\n this.locks[keyWithPrefix] = lock;\n }\n\n return JSON.parse(value);\n };\n\n public readonly getMultiple = async <T = any>(keys: string[]): Promise<T[]> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n let values;\n try {\n if (keysWithPrefix.length === 0) {\n return [];\n }\n // Try to get the value from redis.\n values = await this.client.mGet(keysWithPrefix);\n } catch (err) {\n throw new Error('Failed to get a value', { cause: err });\n }\n return values.map(value => JSON.parse(value));\n };\n\n public readonly set = async <T = any>(key: string, value: T): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n const ttl = parseInt(String(this.baseTTL + (Math.random() + 1)), 10);\n try {\n await this.client.set(keyWithPrefix, JSON.stringify(value), { EX: ttl });\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n } catch (err) {\n throw new Error('Failed to set a key-value pair', { cause: err });\n }\n };\n\n public readonly setMultiple = async <T = any>(keyValues: Record<string, T>): Promise<void> => {\n if (typeof keyValues !== 'object') {\n const error = new Error('keyValues must be an object');\n this.logger.error('keyValues must be an object', { error });\n throw error;\n }\n\n if (keyValues === null || keyValues === undefined || Object.keys(keyValues).length === 0) {\n return undefined;\n }\n\n const keyValuesWithPrefix = Object.entries(keyValues).map<[string, string]>(([key, value]) => [KEY_PREFIX + key, JSON.stringify(value)]);\n const ttl = Math.trunc(this.baseTTL * (Math.random() + 1));\n try {\n const multi = this.client.multi();\n const setPromise = multi.mSet(keyValuesWithPrefix);\n keyValuesWithPrefix.map(([key]) => multi.expire(key, ttl));\n await multi.exec();\n // @ts-expect-error we are returning a value although the type definition says void\n return setPromise;\n } catch (err) {\n throw new Error('Failed to set multiple key-value pairs', { cause: err });\n }\n };\n\n public readonly remove = async (key: string): Promise<void> => {\n const keyWithPrefix = KEY_PREFIX + key;\n try {\n if (this.locks[keyWithPrefix]) {\n await this.locks[keyWithPrefix]();\n delete this.locks[keyWithPrefix];\n }\n\n await this.client.del(keyWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete key ${keyWithPrefix}`, { cause: err });\n }\n };\n\n public readonly removeMultiple = async (keys: string[]): Promise<void> => {\n const keysWithPrefix = keys.map(key => KEY_PREFIX + key);\n try {\n await this.client.del(keysWithPrefix);\n } catch (err) {\n throw new Error(`Failed to delete multiple keys ${keysWithPrefix.join('|')}`, { cause: err });\n }\n };\n\n public readonly getClient = (): typeof this.client => this.client;\n\n private lock(key: string): Promise<() => Promise<void>> {\n return Promise.race([\n this.locker(key, this.lockDuration),\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n new Promise<never>((_resolve, reject) => setTimeout(() => reject(), this.lockTimeout)),\n ]);\n }\n}\n\nexport default RedisCache;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/matmon",
3
- "version": "3.0.13",
3
+ "version": "3.0.14",
4
4
  "description": "manage cache",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -21,14 +21,6 @@
21
21
  "engines": {
22
22
  "node": ">=18"
23
23
  },
24
- "scripts": {
25
- "build": "tsdown",
26
- "prepublish": "npm run build",
27
- "coverage": "vitest --coverage",
28
- "test": "vitest run",
29
- "test-auto": "vitest run --watch",
30
- "linter": "./node_modules/.bin/eslint ."
31
- },
32
24
  "repository": {
33
25
  "type": "git",
34
26
  "url": "git+https://github.com/Autofleet/autorepo.git"
@@ -46,15 +38,23 @@
46
38
  "redis-lock": "^1.0.0"
47
39
  },
48
40
  "devDependencies": {
49
- "@autofleet/logger": "^4.0.3",
50
41
  "@oxc-project/runtime": "^0.82.3",
51
42
  "@types/lru-cache": "^5.1.1",
52
- "@types/node": "^20.17.10"
43
+ "@types/node": "^20.17.10",
44
+ "@autofleet/logger": "^4.2.33"
53
45
  },
54
46
  "peerDependencies": {
55
47
  "@autofleet/logger": "^4"
56
48
  },
57
49
  "files": [
58
50
  "lib/**/*"
59
- ]
60
- }
51
+ ],
52
+ "scripts": {
53
+ "build": "tsdown",
54
+ "prepublish": "npm run build",
55
+ "coverage": "vitest --coverage",
56
+ "test": "vitest run",
57
+ "test-auto": "vitest run --watch",
58
+ "linter": "./node_modules/.bin/eslint ."
59
+ }
60
+ }