Skip to content

Commit 33eaecb

Browse files
committed
implement rediscache fixes, select cache based on .env setting CACHE_DRIVER which can be either memory or redis
1 parent 5784d19 commit 33eaecb

File tree

7 files changed

+98
-70
lines changed

7 files changed

+98
-70
lines changed

src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,9 @@ require('dotenv').config();
55

66
const port = parseInt(process.env.PORT) || 3000;
77
const isDevelopmentMode = process.env.NODE_ENV === 'development';
8+
const cacheDriver = (process.env.CACHE_DRIVER || 'memory').toLowerCase();
89

9-
getServer(new App(), isDevelopmentMode).listen(port, () => console.log(`Listening on http://localhost:${port}`));
10+
getServer(new App(), isDevelopmentMode, cacheDriver).listen(port, () => {
11+
console.log(`Listening on http://localhost:${port}`);
12+
console.log(`Cache driver: ${cacheDriver}`);
13+
});

src/lib/cache/RedisCache.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class RedisCache extends Cache {
1313

1414
this.prefix = keyPrefix;
1515

16-
this.redis = new Redis({ keyPrefix: keyPrefix });
16+
this.redis = new Redis(); //{ keyPrefix: keyPrefix });
1717
}
1818

1919
public destroy() {
@@ -27,13 +27,9 @@ export class RedisCache extends Cache {
2727
}
2828

2929
public async get(key: string): Promise<any | null> {
30-
let result = await this.redis.get(key);
30+
const result = await this.redis.get(key);
3131

32-
if (result !== null) {
33-
result = JSON.parse(result);
34-
}
35-
36-
return new Promise(resolve => resolve(result));
32+
return new Promise(resolve => resolve(JSON.parse(result)));
3733
}
3834

3935
public put(key: string, value: any, ttlSeconds: number): void {

src/lib/resolver.ts

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { MemoryCache } from '@/lib/cache/MemoryCache';
1+
import { RedisCache } from './cache/RedisCache';
2+
import { MemoryCache } from './cache/MemoryCache';
23
import { getResolver as getCommentsResolver } from '@/resolvers/comments';
34
import { getResolver as getJobsResolver } from '@/resolvers/jobs';
45
import { getResolver as getStoriesResolver } from '@/resolvers/stories';
@@ -7,49 +8,51 @@ import { getResolver as getCommentCountResolver } from '@/resolvers/Story/commen
78
import { getResolver as getUserResolver } from '@/resolvers/user';
89
import { getScalarType as getDateScalar } from '@/scalars/Date';
910

10-
const storyIds: number[] = [];
11-
const stories: Record<string, any>[] = [];
12-
const jobIds: number[] = [];
13-
const jobs: Record<string, any>[] = [];
14-
const cache = new MemoryCache();
11+
export const getResolvers = (cacheDriver: string) => {
12+
const storyIds: number[] = [];
13+
const stories: Record<string, any>[] = [];
14+
const jobIds: number[] = [];
15+
const jobs: Record<string, any>[] = [];
16+
const cache = cacheDriver === 'redis' ? new RedisCache() : new MemoryCache();
1517

16-
const dateScalar = getDateScalar();
18+
const dateScalar = getDateScalar();
1719

18-
const resolvers = {
19-
query: {
20-
stories: getStoriesResolver(storyIds, stories, cache),
21-
jobs: getJobsResolver(jobIds, jobs, cache),
22-
user: getUserResolver(cache),
23-
},
24-
job: {
25-
author: getStoryAuthorResolver(cache),
26-
},
27-
story: {
28-
author: getStoryAuthorResolver(cache),
29-
comments: getCommentsResolver(cache),
30-
commentCount: getCommentCountResolver(cache),
31-
},
32-
comment: {
33-
author: getStoryAuthorResolver(cache),
34-
},
35-
};
20+
const resolvers = {
21+
query: {
22+
stories: getStoriesResolver(storyIds, stories, cache),
23+
jobs: getJobsResolver(jobIds, jobs, cache),
24+
user: getUserResolver(cache),
25+
},
26+
job: {
27+
author: getStoryAuthorResolver(cache),
28+
},
29+
story: {
30+
author: getStoryAuthorResolver(cache),
31+
comments: getCommentsResolver(cache),
32+
commentCount: getCommentCountResolver(cache),
33+
},
34+
comment: {
35+
author: getStoryAuthorResolver(cache),
36+
},
37+
};
3638

37-
export default {
38-
Date: dateScalar,
39-
Query: {
40-
jobs: resolvers.query.jobs,
41-
stories: resolvers.query.stories,
42-
user: resolvers.query.user,
43-
},
44-
Job: {
45-
author: resolvers.job.author,
46-
},
47-
Story: {
48-
author: resolvers.story.author,
49-
comments: resolvers.story.comments,
50-
commentCount: resolvers.story.commentCount,
51-
},
52-
Comment: {
53-
author: resolvers.comment.author,
54-
},
39+
return {
40+
Date: dateScalar,
41+
Query: {
42+
jobs: resolvers.query.jobs,
43+
stories: resolvers.query.stories,
44+
user: resolvers.query.user,
45+
},
46+
Job: {
47+
author: resolvers.job.author,
48+
},
49+
Story: {
50+
author: resolvers.story.author,
51+
comments: resolvers.story.comments,
52+
commentCount: resolvers.story.commentCount,
53+
},
54+
Comment: {
55+
author: resolvers.comment.author,
56+
},
57+
};
5558
};

src/lib/server.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { ApolloServer } from 'apollo-server-express';
22
import bodyParser from 'body-parser';
33
import typeDefs from './typedef';
4-
import resolvers from './resolver';
4+
import { getResolvers } from './resolver';
5+
6+
export const getApolloServer = (isDevelopmentMode, cacheDriver) => {
7+
const resolvers = getResolvers(cacheDriver);
58

6-
export const getApolloServer = isDevelopmentMode => {
79
const server = new ApolloServer({
810
introspection: isDevelopmentMode,
911
graphiql: isDevelopmentMode,
@@ -20,10 +22,10 @@ export const getApolloServer = isDevelopmentMode => {
2022
return server;
2123
};
2224

23-
export const getServer = (app, isDevelopmentMode) => {
25+
export const getServer = (app, isDevelopmentMode, cacheDriver) => {
2426
app.use(bodyParser.json());
2527

26-
const server = getApolloServer(isDevelopmentMode);
28+
const server = getApolloServer(isDevelopmentMode, cacheDriver);
2729
server.applyMiddleware({ app, path: '/graphql' });
2830

2931
return app;

src/resolvers/jobs.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { MemoryCache } from '@/lib/cache/MemoryCache';
1+
import { Cache } from '@/lib/cache/Cache';
22
import axios from 'axios';
33

4-
export function getResolver(storyIds, stories, cache: MemoryCache): any {
4+
export function getResolver(storyIds, stories, cache: Cache): any {
55
const result = async (_, { first, skipText }) => {
66
const kind = 'job';
77

@@ -13,6 +13,8 @@ export function getResolver(storyIds, stories, cache: MemoryCache): any {
1313

1414
const hasKey = await cache.has(cacheKey);
1515
if (!hasKey) {
16+
console.log(`getting ${kind}storyids from URL...`);
17+
1618
const resp = await axios.get(`${process.env.HACKERNEWS_API_URL}/jobstories.json?limitToFirst=${first + 5}&orderBy="$key"`);
1719
const data: number[] = resp.data;
1820

@@ -28,20 +30,26 @@ export function getResolver(storyIds, stories, cache: MemoryCache): any {
2830
const storyDataPromises = ids
2931
.map(async id => {
3032
if (await cache.has(`${kind}story:${id}`)) {
31-
return null;
33+
return cache.get(`${kind}story:${id}`);
3234
}
3335

36+
console.log(`getting story ${id} from URL...`);
37+
3438
return axios.get(`${process.env.HACKERNEWS_API_URL}/item/${id}.json`);
3539
})
3640
.filter(item => item !== null);
3741

3842
(await Promise.all(storyDataPromises))
39-
.map(resp => resp?.data)
43+
.map((resp: any) => resp?.data)
4044
.filter(item => item !== null)
45+
.filter(item => typeof item !== 'undefined')
4146
.forEach(item => {
42-
cache.put(`${kind}story:${item.id}`, item, 3600);
47+
try {
48+
cache.put(`${kind}story:${item.id}`, item, 3600);
49+
} catch (err) {
50+
console.log(err);
51+
}
4352
});
44-
4553
for (const id of ids) {
4654
const storyItem = await cache.get(`${kind}story:${id}`);
4755

src/resolvers/stories.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,20 @@ export function getResolver(topStoryIds, topStories, cache: any): any {
1212

1313
const hasTopStoryKey = await cache.has(topstoryCacheKey);
1414
if (!hasTopStoryKey) {
15-
// console.log(`getting ${kind}storyids from URL...`);
15+
console.log(`getting ${kind}storyids from URL...`);
1616

1717
const resp = await axios.get(`${process.env.HACKERNEWS_API_URL}/${kind}stories.json?limitToFirst=${first}&orderBy="$key"`);
1818
const data: number[] = resp.data;
1919

20-
cache.put(topstoryCacheKey, data, 60);
20+
const cacheTtlMap = {
21+
new: 60,
22+
top: 300,
23+
best: 600,
24+
};
25+
26+
const cacheTtl = cacheTtlMap[kind] || 60;
27+
28+
cache.put(topstoryCacheKey, data, cacheTtl);
2129
ids.push(...data);
2230
}
2331

@@ -29,24 +37,31 @@ export function getResolver(topStoryIds, topStories, cache: any): any {
2937
const storyDataPromises = ids
3038
.map(async id => {
3139
if (await cache.has(`${kind}story:${id}`)) {
32-
return null;
40+
return cache.get(`${kind}story:${id}`);
3341
}
3442

35-
// console.log(`getting story ${id} from URL...`);
43+
console.log(`getting story ${id} from URL...`);
3644

3745
return axios.get(`${process.env.HACKERNEWS_API_URL}/item/${id}.json`);
3846
})
3947
.filter(item => item !== null);
4048

4149
(await Promise.all(storyDataPromises))
42-
.map(resp => resp?.data)
50+
.map((resp: any) => resp?.data)
4351
.filter(item => item !== null)
52+
.filter(item => typeof item !== 'undefined')
4453
.forEach(item => {
45-
cache.put(`${kind}story:${item.id}`, item, 600);
54+
try {
55+
cache.put(`${kind}story:${item.id}`, item, 600);
56+
} catch (err) {
57+
console.log(err);
58+
}
4659
});
4760

4861
for (const id of ids) {
49-
topStories.push(await cache.get(`${kind}story:${id}`));
62+
const item = await cache.get(`${kind}story:${id}`);
63+
64+
topStories.push(item);
5065
}
5166

5267
return new Promise(resolve => resolve(topStories));

src/resolvers/user.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/* eslint-disable no-unused-vars */
22

3-
import { MemoryCache } from '../lib/cache/MemoryCache';
3+
import { Cache } from '../lib/cache/Cache';
44
import axios from 'axios';
55

6-
export function getResolver(cache: MemoryCache): any {
6+
export function getResolver(cache: Cache): any {
77
const userResolver = async (_, { id }) => {
88
const hasKey = await cache.has(`user:${id}`);
99

0 commit comments

Comments
 (0)