Skip to content

Commit

Permalink
Implement TTL and use our own repository to cache requests (#83)
Browse files Browse the repository at this point in the history
* Implement TTL and use our own repository to cache requests

* Remove unused import
  • Loading branch information
anxolin authored Aug 9, 2024
1 parent 5862714 commit 2fa7b68
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 17 deletions.
36 changes: 20 additions & 16 deletions apps/api/src/app/plugins/bffCache.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -38,16 +40,18 @@ export const bffCache: FastifyPluginCallback<BffCacheOptions> = (
// 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(
Expand Down Expand Up @@ -91,7 +95,7 @@ export const bffCache: FastifyPluginCallback<BffCacheOptions> = (
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(
Expand Down
1 change: 1 addition & 0 deletions libs/repositories/src/CacheRepository/CacheRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export const cacheRepositorySymbol = Symbol.for('CacheRepository');

export interface CacheRepository {
get(key: string): Promise<string | null>;
getTtl(key: string): Promise<number | null>;
set(key: string, value: string, ttl: number): Promise<void>;
}
10 changes: 10 additions & 0 deletions libs/repositories/src/CacheRepository/CacheRepositoryMemory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ export class CacheRepositoryMemory implements CacheRepository {
return value ?? null;
}

async getTtl(key: string): Promise<number | null> {
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<void> {
await CacheRepositoryMemory.cache.set(key, value, ttl);
}
Expand Down
13 changes: 12 additions & 1 deletion libs/repositories/src/CacheRepository/CacheRepositoryRedis.ts
Original file line number Diff line number Diff line change
@@ -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<string | null> {
return this.redisClient.get(key);
}

async getTtl(key: string): Promise<number | null> {
const ttl = await this.redisClient.ttl(key);

if (ttl < 0) {
return null;
}

return ttl;
}

async set(key: string, value: string, ttl: number): Promise<void> {
await this.redisClient.set(key, value, 'EX', ttl);
}
Expand Down

0 comments on commit 2fa7b68

Please sign in to comment.