Skip to content

Commit 820f5d9

Browse files
committed
warm cache
1 parent 4d0ce02 commit 820f5d9

File tree

5 files changed

+70
-7
lines changed

5 files changed

+70
-7
lines changed

src/server/cron.ts

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { runAdaptersFromTo } from "./jobs/runAdaptersFromTo";
55
import { handler as runWormhole } from "../handlers/runWormhole";
66
import { aggregateHourlyVolume } from "./jobs/aggregateHourlyVolume";
77
import { aggregateDailyVolume } from "./jobs/aggregateDailyVolume";
8+
import { warmAllCaches } from "./jobs/warmCache";
89

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

2324
const cron = () => {
25+
if (process.env.NO_CRON) {
26+
return;
27+
}
28+
2429
new CronJob("15,30,45 * * * *", async () => {
2530
await withTimeout(runAllAdapters(), 10);
2631
}).start();
@@ -44,6 +49,10 @@ const cron = () => {
4449
new CronJob("35 * * * *", async () => {
4550
await withTimeout(aggregateDailyVolume(), 20);
4651
}).start();
52+
53+
new CronJob("*/5 * * * *", async () => {
54+
await withTimeout(warmAllCaches(), 4);
55+
}).start();
4756
};
4857

4958
export default cron;

src/server/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import getTransactions from "../handlers/getTransactions";
1212
import runAdapter from "../handlers/runAdapter";
1313
import getBridgeStatsOnDay from "../handlers/getBridgeStatsOnDay";
1414
import cron from "./cron";
15-
import { generateApiCacheKey, cache } from "../utils/cache";
15+
import { generateApiCacheKey, cache, registerCacheHandler, warmCache, needsWarming } from "../utils/cache";
1616

1717
dotenv.config();
1818

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

4343
const cacheKey = generateApiCacheKey(event);
4444
const cachedData = cache.get(cacheKey);
45+
46+
registerCacheHandler(cacheKey, () => handler(event));
47+
4548
if (cachedData) {
49+
if (needsWarming(cacheKey)) {
50+
warmCache(cacheKey);
51+
}
4652
return reply.code(200).send(cachedData);
4753
}
4854

src/server/jobs/warmCache.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { warmCache, handlerRegistry, needsWarming } from "../../utils/cache";
2+
3+
const warmAllCaches = async () => {
4+
const cacheKeys = Array.from(handlerRegistry.keys());
5+
6+
for (const cacheKey of cacheKeys) {
7+
try {
8+
if (needsWarming(cacheKey)) {
9+
await warmCache(cacheKey);
10+
await new Promise((resolve) => setTimeout(resolve, 1000));
11+
}
12+
} catch (error) {
13+
console.error(`Failed to warm cache for key ${cacheKey}:`, error);
14+
}
15+
}
16+
};
17+
18+
export { warmAllCaches };

src/utils/cache.ts

+35-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import { LRUCache } from "lru-cache";
22
import hash from "object-hash";
33

4-
export const cache = new LRUCache({
5-
max: 1000,
6-
ttl: 1000 * 60 * 60,
7-
});
8-
94
interface APIEvent {
105
pathParameters?: Record<string, any>;
116
queryStringParameters?: Record<string, any>;
127
body?: any;
138
}
149

10+
export const handlerRegistry = new Map<string, Function>();
11+
12+
export const cache = new LRUCache<string, any>({
13+
max: 1000,
14+
ttl: 1000 * 60 * 60,
15+
updateAgeOnGet: false,
16+
});
17+
1518
export const generateApiCacheKey = (event: APIEvent): string => {
1619
const eventToNormalize = {
1720
path: event.pathParameters || {},
@@ -27,6 +30,33 @@ export const generateApiCacheKey = (event: APIEvent): string => {
2730
}).substring(0, 16);
2831
};
2932

33+
export const CACHE_WARM_THRESHOLD = 1000 * 60 * 10;
34+
35+
export const needsWarming = (cacheKey: string): boolean => {
36+
if (!cache.has(cacheKey)) return true;
37+
38+
const ttlRemaining = cache.getRemainingTTL(cacheKey);
39+
return ttlRemaining !== undefined && ttlRemaining < CACHE_WARM_THRESHOLD;
40+
};
41+
42+
export const warmCache = async (cacheKey: string): Promise<void> => {
43+
const handler = handlerRegistry.get(cacheKey);
44+
if (!handler) {
45+
return;
46+
}
47+
try {
48+
const result = await handler();
49+
const parsedBody = JSON.parse(result.body);
50+
cache.set(cacheKey, parsedBody);
51+
} catch (error) {
52+
throw error;
53+
}
54+
};
55+
56+
export const registerCacheHandler = (cacheKey: string, handler: Function) => {
57+
handlerRegistry.set(cacheKey, handler);
58+
};
59+
3060
export const getCacheKey = (...parts: (string | undefined)[]) => parts.filter(Boolean).join(":");
3161

3262
export const DEFAULT_TTL = 600;

src/utils/runAdapterHistorical.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const runAllAdaptersHistorical = async (startTimestamp: number, endTimestamp: nu
9898
}
9999
};
100100

101-
if (bridgeName && chain) {
101+
if (bridgeName) {
102102
fillAdapterHistorical(startTs, endTs, bridgeName, chain);
103103
} else {
104104
runAllAdaptersHistorical(startTs, endTs);

0 commit comments

Comments
 (0)