From 2fa7b6848a06ac7e801b3e421eec0cca07fd614c Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Fri, 9 Aug 2024 11:49:21 +0100 Subject: [PATCH] Implement TTL and use our own repository to cache requests (#83) * Implement TTL and use our own repository to cache requests * Remove unused import --- apps/api/src/app/plugins/bffCache.ts | 36 ++++++++++--------- .../src/CacheRepository/CacheRepository.ts | 1 + .../CacheRepository/CacheRepositoryMemory.ts | 10 ++++++ .../CacheRepository/CacheRepositoryRedis.ts | 13 ++++++- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/apps/api/src/app/plugins/bffCache.ts b/apps/api/src/app/plugins/bffCache.ts index c5117e3..aca5fb4 100644 --- a/apps/api/src/app/plugins/bffCache.ts +++ b/apps/api/src/app/plugins/bffCache.ts @@ -1,21 +1,23 @@ import fp from 'fastify-plugin'; import { CACHE_CONTROL_HEADER, - getCache, getCacheControlHeaderValue, parseCacheControlHeaderValue, - setCache, } from '../../utils/cache'; -import { - FastifyInstance, - FastifyPluginCallback, - FastifyReply, - FastifyRequest, -} from 'fastify'; +import { FastifyPluginCallback, FastifyReply, FastifyRequest } from 'fastify'; const HEADER_NAME = 'x-bff-cache'; -import { getCacheKey } from '@cowprotocol/repositories'; +import { + CacheRepository, + cacheRepositorySymbol, + getCacheKey, +} from '@cowprotocol/repositories'; import { Readable } from 'stream'; +import { apiContainer } from '../inversify.config'; + +const cacheRepository: CacheRepository = apiContainer.get( + cacheRepositorySymbol +); interface BffCacheOptions { ttl?: number; @@ -38,16 +40,18 @@ export const bffCache: FastifyPluginCallback = ( // Remove it so we can cache it properly request.headers['accept-encoding'] = undefined; - const cacheItem = await getCache(key, fastify).catch((e) => { + const [item, ttl] = await Promise.all([ + cacheRepository.get(key), + cacheRepository.getTtl(key), + ]).catch((e) => { fastify.log.error(`Error getting key ${key} from cache`, e); - return undefined; + return [null, null]; }); - if (cacheItem) { - const { item, ttl } = cacheItem; - fastify.log.debug(`Found cached item "${item}" with TTL`, ttl); + if (item !== null && ttl !== null) { + fastify.log.debug(`Found cached item "${item}" with TTL ${ttl}`); - const ttlInSeconds = isNaN(ttl) ? undefined : Math.floor(ttl / 1000); + const ttlInSeconds = isNaN(ttl) ? undefined : ttl; if (ttlInSeconds !== undefined) { reply.header( @@ -91,7 +95,7 @@ export const bffCache: FastifyPluginCallback = ( if (content !== null) { // Cache (fire and forget) const key = getKey(req); - setCache(key, content, cacheTtl, fastify).catch((e) => { + cacheRepository.set(key, content, cacheTtl).catch((e) => { fastify.log.error(`Error setting key ${key} to cache`, e); }); reply.header( diff --git a/libs/repositories/src/CacheRepository/CacheRepository.ts b/libs/repositories/src/CacheRepository/CacheRepository.ts index 6aa094c..78107c4 100644 --- a/libs/repositories/src/CacheRepository/CacheRepository.ts +++ b/libs/repositories/src/CacheRepository/CacheRepository.ts @@ -2,5 +2,6 @@ export const cacheRepositorySymbol = Symbol.for('CacheRepository'); export interface CacheRepository { get(key: string): Promise; + getTtl(key: string): Promise; set(key: string, value: string, ttl: number): Promise; } diff --git a/libs/repositories/src/CacheRepository/CacheRepositoryMemory.ts b/libs/repositories/src/CacheRepository/CacheRepositoryMemory.ts index e9bbe30..501fadc 100644 --- a/libs/repositories/src/CacheRepository/CacheRepositoryMemory.ts +++ b/libs/repositories/src/CacheRepository/CacheRepositoryMemory.ts @@ -12,6 +12,16 @@ export class CacheRepositoryMemory implements CacheRepository { return value ?? null; } + async getTtl(key: string): Promise { + const ttlEpoch = (await CacheRepositoryMemory.cache.getTtl(key)) || null; + + if (ttlEpoch === null) { + return null; + } + + return Math.floor((ttlEpoch - Date.now()) / 1000); + } + async set(key: string, value: string, ttl: number): Promise { await CacheRepositoryMemory.cache.set(key, value, ttl); } diff --git a/libs/repositories/src/CacheRepository/CacheRepositoryRedis.ts b/libs/repositories/src/CacheRepository/CacheRepositoryRedis.ts index 4e69e12..f9c250b 100644 --- a/libs/repositories/src/CacheRepository/CacheRepositoryRedis.ts +++ b/libs/repositories/src/CacheRepository/CacheRepositoryRedis.ts @@ -1,14 +1,25 @@ import { injectable } from 'inversify'; import { CacheRepository } from './CacheRepository'; +import { Redis } from 'ioredis'; @injectable() export class CacheRepositoryRedis implements CacheRepository { - constructor(private redisClient: any) {} + constructor(private redisClient: Redis) {} async get(key: string): Promise { return this.redisClient.get(key); } + async getTtl(key: string): Promise { + const ttl = await this.redisClient.ttl(key); + + if (ttl < 0) { + return null; + } + + return ttl; + } + async set(key: string, value: string, ttl: number): Promise { await this.redisClient.set(key, value, 'EX', ttl); }