Skip to content

Commit 1cd51f7

Browse files
committed
Enhance caching by adding refreshCacheFromDB function and updating eatery data retrieval logic, also refresh cache on startup
1 parent 7ee261f commit 1cd51f7

4 files changed

Lines changed: 78 additions & 29 deletions

File tree

src/eateries/eateries.schema.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export const getAllEateriesSchema = z.object({
77
});
88

99
// Schema for validating cached eatery data
10-
export const EaterySchema = z.array(z.any()); // This will be validated by the scraper that sends the data
10+
// This will be validated by the scraper that sends the data, max 500 eateries for safety
11+
export const EaterySchema = z.array(z.any()).max(500);

src/eateries/eateryService.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import type { Event } from '@prisma/client';
22

33
import { prisma } from '../prisma.js';
44
import { NotFoundError } from '../utils/AppError.js';
5-
import { getAllEateriesData } from '../utils/cache.js';
5+
import { getAllEateriesData, refreshCacheFromDB } from '../utils/cache.js';
6+
import type { EateryWithEvents } from '../utils/cache.js';
67

78
export const getAllEateries = async (days: number = 0) => {
89
// Calculate date range for filtering events
@@ -22,36 +23,26 @@ export const getAllEateries = async (days: number = 0) => {
2223
const cachedEateries = getAllEateriesData();
2324

2425
// Filter events to only include those on the specified day
25-
const filteredEateries = cachedEateries.map((eatery) => ({
26-
...eatery,
27-
events: eatery.events.filter((event: Event) => {
28-
const eventStart = new Date(event.startTimestamp);
29-
const eventEnd = new Date(event.endTimestamp);
30-
31-
// Include event if it overlaps with the target day
32-
return eventStart <= endOfDay && eventEnd >= startOfDay;
33-
}),
34-
}));
26+
const filteredEateries = filterEateries(
27+
cachedEateries,
28+
startOfDay,
29+
endOfDay,
30+
);
3531

3632
return filteredEateries;
3733
} catch {
3834
// If cache is cold (should never happen), fall back to database
39-
const eateries = await prisma.eatery.findMany({
40-
include: {
41-
events: {
42-
where: {
43-
startTimestamp: {
44-
lte: endOfDay,
45-
},
46-
endTimestamp: {
47-
gte: startOfDay,
48-
},
49-
},
50-
},
51-
},
52-
});
35+
refreshCacheFromDB();
36+
const cachedEateries = getAllEateriesData();
37+
38+
// Filter events to only include those on the specified day
39+
const filteredEateries = filterEateries(
40+
cachedEateries,
41+
startOfDay,
42+
endOfDay,
43+
);
5344

54-
return eateries;
45+
return filteredEateries;
5546
}
5647
};
5748

@@ -103,3 +94,21 @@ export const getEateryById = async (eateryId: number) => {
10394
return eatery;
10495
}
10596
};
97+
98+
function filterEateries(
99+
cachedEateries: EateryWithEvents[],
100+
startOfDay: Date,
101+
endOfDay: Date,
102+
) {
103+
const filteredEateries = cachedEateries.map((eatery) => ({
104+
...eatery,
105+
events: eatery.events.filter((event: Event) => {
106+
const eventStart = new Date(event.startTimestamp);
107+
const eventEnd = new Date(event.endTimestamp);
108+
109+
// Include event if it overlaps with the target day
110+
return eventStart <= endOfDay && eventEnd >= startOfDay;
111+
}),
112+
}));
113+
return filteredEateries;
114+
}

src/server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { globalErrorHandler } from './middleware/errorHandler.js';
1010
import { requestLogger } from './middleware/logger.js';
1111
import { ipRateLimiter } from './middleware/rateLimit.js';
1212
import { prisma } from './prisma.js';
13+
import { refreshCacheFromDB } from './utils/cache.js';
1314

1415
const app = express();
1516

@@ -65,6 +66,8 @@ const server = app.listen(port, async () => {
6566
try {
6667
await prisma.$connect();
6768
console.log('Database connected successfully');
69+
await refreshCacheFromDB();
70+
console.log('Cache initialized from database');
6871
} catch (error) {
6972
console.error('Failed to connect to database:', error);
7073
process.exit(1);

src/utils/cache.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@ import NodeCache from 'node-cache';
22

33
import type { Eatery, Event } from '@prisma/client';
44

5-
import { type NextFunction, type Request, type Response, Router } from 'express';
5+
import {
6+
type NextFunction,
7+
type Request,
8+
type Response,
9+
Router,
10+
} from 'express';
611

712
import { EaterySchema } from '../eateries/eateries.schema.js';
13+
import { prisma } from '../prisma.js';
814
import { UnauthorizedError } from './AppError.js';
915

1016
export const cacheRouter = Router();
1117
export const appCache = new NodeCache({ stdTTL: 0 }); // never expire
1218

13-
type EateryWithEvents = Eatery & { events: Event[] };
19+
export type EateryWithEvents = Eatery & { events: Event[] };
1420

1521
function requireSecret(req: Request, _res: Response, next: NextFunction): void {
1622
const provided = req.header(process.env.CACHE_REFRESH_HEADER!);
@@ -50,6 +56,36 @@ cacheRouter.post('/internal/refresh-cache', requireSecret, (req, res) => {
5056
res.status(200).json({ ok: true });
5157
});
5258

59+
export async function refreshCacheFromDB() {
60+
appCache.del('allEateriesData');
61+
const eateries = await prisma.eatery.findMany({
62+
include: {
63+
events: {
64+
include: {
65+
menu: {
66+
include: {
67+
items: {
68+
include: {
69+
dietaryPreferences: true,
70+
allergens: true,
71+
},
72+
},
73+
},
74+
},
75+
userEventVotes: true,
76+
},
77+
orderBy: {
78+
startTimestamp: 'asc',
79+
},
80+
},
81+
},
82+
});
83+
appCache.set('allEateriesData', eateries);
84+
const etag = `"eateries-${Date.now()}"`;
85+
appCache.set('allEateriesEtag', etag);
86+
console.log('Cache updated from DB, allEateriesData refreshed.');
87+
}
88+
5389
export function clearAppCache(): void {
5490
appCache.flushAll();
5591
}

0 commit comments

Comments
 (0)