Skip to content

Commit

Permalink
warm cache
Browse files Browse the repository at this point in the history
  • Loading branch information
vrtnd committed Dec 16, 2024
1 parent 4d0ce02 commit 820f5d9
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 7 deletions.
9 changes: 9 additions & 0 deletions src/server/cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { runAdaptersFromTo } from "./jobs/runAdaptersFromTo";
import { handler as runWormhole } from "../handlers/runWormhole";
import { aggregateHourlyVolume } from "./jobs/aggregateHourlyVolume";
import { aggregateDailyVolume } from "./jobs/aggregateDailyVolume";
import { warmAllCaches } from "./jobs/warmCache";

const createTimeout = (minutes: number) =>
new Promise((_, reject) =>
Expand All @@ -21,6 +22,10 @@ const withTimeout = async (promise: Promise<any>, timeoutMinutes: number) => {
};

const cron = () => {
if (process.env.NO_CRON) {
return;
}

new CronJob("15,30,45 * * * *", async () => {
await withTimeout(runAllAdapters(), 10);
}).start();
Expand All @@ -44,6 +49,10 @@ const cron = () => {
new CronJob("35 * * * *", async () => {
await withTimeout(aggregateDailyVolume(), 20);
}).start();

new CronJob("*/5 * * * *", async () => {
await withTimeout(warmAllCaches(), 4);
}).start();
};

export default cron;
8 changes: 7 additions & 1 deletion src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import getTransactions from "../handlers/getTransactions";
import runAdapter from "../handlers/runAdapter";
import getBridgeStatsOnDay from "../handlers/getBridgeStatsOnDay";
import cron from "./cron";
import { generateApiCacheKey, cache } from "../utils/cache";
import { generateApiCacheKey, cache, registerCacheHandler, warmCache, needsWarming } from "../utils/cache";

dotenv.config();

Expand Down Expand Up @@ -42,7 +42,13 @@ const lambdaToFastify = (handler: Function) => async (request: any, reply: any)

const cacheKey = generateApiCacheKey(event);
const cachedData = cache.get(cacheKey);

registerCacheHandler(cacheKey, () => handler(event));

if (cachedData) {
if (needsWarming(cacheKey)) {
warmCache(cacheKey);
}
return reply.code(200).send(cachedData);
}

Expand Down
18 changes: 18 additions & 0 deletions src/server/jobs/warmCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { warmCache, handlerRegistry, needsWarming } from "../../utils/cache";

const warmAllCaches = async () => {
const cacheKeys = Array.from(handlerRegistry.keys());

for (const cacheKey of cacheKeys) {
try {
if (needsWarming(cacheKey)) {
await warmCache(cacheKey);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
} catch (error) {
console.error(`Failed to warm cache for key ${cacheKey}:`, error);
}
}
};

export { warmAllCaches };
40 changes: 35 additions & 5 deletions src/utils/cache.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { LRUCache } from "lru-cache";
import hash from "object-hash";

export const cache = new LRUCache({
max: 1000,
ttl: 1000 * 60 * 60,
});

interface APIEvent {
pathParameters?: Record<string, any>;
queryStringParameters?: Record<string, any>;
body?: any;
}

export const handlerRegistry = new Map<string, Function>();

export const cache = new LRUCache<string, any>({
max: 1000,
ttl: 1000 * 60 * 60,
updateAgeOnGet: false,
});

export const generateApiCacheKey = (event: APIEvent): string => {
const eventToNormalize = {
path: event.pathParameters || {},
Expand All @@ -27,6 +30,33 @@ export const generateApiCacheKey = (event: APIEvent): string => {
}).substring(0, 16);
};

export const CACHE_WARM_THRESHOLD = 1000 * 60 * 10;

export const needsWarming = (cacheKey: string): boolean => {
if (!cache.has(cacheKey)) return true;

const ttlRemaining = cache.getRemainingTTL(cacheKey);
return ttlRemaining !== undefined && ttlRemaining < CACHE_WARM_THRESHOLD;
};

export const warmCache = async (cacheKey: string): Promise<void> => {
const handler = handlerRegistry.get(cacheKey);
if (!handler) {
return;
}
try {
const result = await handler();
const parsedBody = JSON.parse(result.body);
cache.set(cacheKey, parsedBody);
} catch (error) {
throw error;
}
};

export const registerCacheHandler = (cacheKey: string, handler: Function) => {
handlerRegistry.set(cacheKey, handler);
};

export const getCacheKey = (...parts: (string | undefined)[]) => parts.filter(Boolean).join(":");

export const DEFAULT_TTL = 600;
2 changes: 1 addition & 1 deletion src/utils/runAdapterHistorical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const runAllAdaptersHistorical = async (startTimestamp: number, endTimestamp: nu
}
};

if (bridgeName && chain) {
if (bridgeName) {
fillAdapterHistorical(startTs, endTs, bridgeName, chain);
} else {
runAllAdaptersHistorical(startTs, endTs);
Expand Down

0 comments on commit 820f5d9

Please sign in to comment.