redis rework/error handling
This commit is contained in:
parent
ec6f266b48
commit
19f9c69ffd
6 changed files with 123 additions and 79 deletions
|
@ -1,8 +1,7 @@
|
|||
import Redis from "ioredis";
|
||||
|
||||
let redis = new Redis(process.env.REDIS_URL);
|
||||
import type RedisInstance from "ioredis";
|
||||
|
||||
async function applyCache(
|
||||
redis: RedisInstance,
|
||||
key: string,
|
||||
query: string,
|
||||
gql: boolean,
|
||||
|
@ -11,7 +10,7 @@ async function applyCache(
|
|||
if (await redis.get(key)) {
|
||||
return JSON.parse((await redis.get(key)) as string);
|
||||
} else {
|
||||
const response = await fetchEndpoint(query, gql);
|
||||
const response = await fetchEndpoint(redis, query, gql);
|
||||
if (response != null) {
|
||||
await redis.set(key, JSON.stringify(response), "EX", cacheTime);
|
||||
}
|
||||
|
@ -19,7 +18,11 @@ async function applyCache(
|
|||
}
|
||||
}
|
||||
|
||||
async function fetchEndpoint(query: string, gql: boolean = false) {
|
||||
async function fetchEndpoint(
|
||||
redis: RedisInstance,
|
||||
query: string,
|
||||
gql: boolean = false
|
||||
) {
|
||||
if (await redis.get("7TV.RATE_LIMIT")) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
} else {
|
||||
|
@ -50,7 +53,7 @@ async function fetchGQL(query: string) {
|
|||
return json;
|
||||
}
|
||||
|
||||
async function getGlobalEmotes() {
|
||||
async function getGlobalEmotes(redis: RedisInstance) {
|
||||
const gqlQuery = `query {
|
||||
namedEmoteSet(name: GLOBAL) {
|
||||
emote_count
|
||||
|
@ -78,10 +81,10 @@ async function getGlobalEmotes() {
|
|||
}
|
||||
}
|
||||
}`;
|
||||
return await applyCache("7TV.GLOBAL_EMOTES", gqlQuery, true, 3600);
|
||||
return await applyCache(redis, "7TV.GLOBAL_EMOTES", gqlQuery, true, 3600);
|
||||
}
|
||||
|
||||
async function getChannelEmotes(channelID: string) {
|
||||
async function getChannelEmotes(redis: RedisInstance, channelID: string) {
|
||||
const gqlQuery = `query {
|
||||
user(id: "${channelID}") {
|
||||
emote_sets {
|
||||
|
@ -112,6 +115,7 @@ async function getChannelEmotes(channelID: string) {
|
|||
}
|
||||
}`;
|
||||
return await applyCache(
|
||||
redis,
|
||||
"7TV.CHANNEL_EMOTES_" + channelID,
|
||||
gqlQuery,
|
||||
true,
|
||||
|
|
55
misc/redis.ts
Normal file
55
misc/redis.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
// https://makerkit.dev/blog/tutorials/nextjs-redis (I got tired of having redis issues)
|
||||
import Redis, { RedisOptions } from "ioredis";
|
||||
|
||||
const configuration = {
|
||||
redis: {
|
||||
host: process.env.REDIS_HOST ?? "localhost",
|
||||
port: parseInt(process.env.REDIS_PORT ?? "6379"),
|
||||
password: process.env.REDIS_PASSWORD ?? null,
|
||||
},
|
||||
};
|
||||
|
||||
function getRedisConfiguration(): {
|
||||
port: number;
|
||||
host: string;
|
||||
password: string | null;
|
||||
} {
|
||||
return configuration.redis;
|
||||
}
|
||||
|
||||
export function createRedisInstance(config = getRedisConfiguration()) {
|
||||
try {
|
||||
const options: RedisOptions = {
|
||||
host: config.host,
|
||||
lazyConnect: true,
|
||||
showFriendlyErrorStack: true,
|
||||
enableAutoPipelining: true,
|
||||
maxRetriesPerRequest: 0,
|
||||
retryStrategy: (times: number) => {
|
||||
if (times > 3) {
|
||||
throw new Error(`[Redis] Could not connect after ${times} attempts`);
|
||||
}
|
||||
|
||||
return Math.min(times * 200, 1000);
|
||||
},
|
||||
};
|
||||
|
||||
if (config.port) {
|
||||
options.port = config.port;
|
||||
}
|
||||
|
||||
if (config.password) {
|
||||
options.password = config.password;
|
||||
}
|
||||
|
||||
const redis = new Redis(options);
|
||||
|
||||
redis.on("error", (error: unknown) => {
|
||||
console.warn("[Redis] Error connecting", error);
|
||||
});
|
||||
|
||||
return redis;
|
||||
} catch (e) {
|
||||
throw new Error(`[Redis] Could not create a Redis instance`);
|
||||
}
|
||||
}
|
101
package-lock.json
generated
101
package-lock.json
generated
|
@ -11,7 +11,7 @@
|
|||
"eslint": "8.28.0",
|
||||
"eslint-config-next": "13.0.4",
|
||||
"framer-motion": "^7.6.19",
|
||||
"ioredis": "^4.28.5",
|
||||
"ioredis": "^5.2.5",
|
||||
"next": "13.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
@ -122,6 +122,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
|
||||
},
|
||||
"node_modules/@motionone/animation": {
|
||||
"version": "10.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz",
|
||||
|
@ -1342,9 +1347,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
||||
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
|
@ -2515,38 +2520,28 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz",
|
||||
"integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==",
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.5.tgz",
|
||||
"integrity": "sha512-7HKo/ClM2DGLRXdFq8ruS3Uuadensz4A76wPOU0adqlOqd1qkhoLPDaBhmVhUhNGpB+J65/bhLmNB8DDY99HJQ==",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.1",
|
||||
"denque": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.0.1",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"p-map": "^2.1.0",
|
||||
"redis-commands": "1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ioredis"
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis/node_modules/p-map": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
|
||||
"integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
|
@ -2841,9 +2836,9 @@
|
|||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -3050,11 +3045,6 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
|
||||
},
|
||||
"node_modules/lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
|
||||
},
|
||||
"node_modules/lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
|
@ -4025,11 +4015,6 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-commands": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
|
||||
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
|
||||
},
|
||||
"node_modules/redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
|
@ -5075,6 +5060,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
|
||||
},
|
||||
"@motionone/animation": {
|
||||
"version": "10.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz",
|
||||
|
@ -5868,9 +5858,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"denque": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
||||
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "2.0.1",
|
||||
|
@ -6728,28 +6718,19 @@
|
|||
}
|
||||
},
|
||||
"ioredis": {
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz",
|
||||
"integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==",
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.2.5.tgz",
|
||||
"integrity": "sha512-7HKo/ClM2DGLRXdFq8ruS3Uuadensz4A76wPOU0adqlOqd1qkhoLPDaBhmVhUhNGpB+J65/bhLmNB8DDY99HJQ==",
|
||||
"requires": {
|
||||
"@ioredis/commands": "^1.1.1",
|
||||
"cluster-key-slot": "^1.1.0",
|
||||
"debug": "^4.3.1",
|
||||
"denque": "^1.1.0",
|
||||
"debug": "^4.3.4",
|
||||
"denque": "^2.0.1",
|
||||
"lodash.defaults": "^4.2.0",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.isarguments": "^3.1.0",
|
||||
"p-map": "^2.1.0",
|
||||
"redis-commands": "1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0",
|
||||
"standard-as-callback": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-map": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
|
||||
"integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-arrayish": {
|
||||
|
@ -6940,9 +6921,9 @@
|
|||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
|
@ -7094,11 +7075,6 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
|
||||
},
|
||||
"lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
|
||||
},
|
||||
"lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
|
@ -7736,11 +7712,6 @@
|
|||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"redis-commands": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
|
||||
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
|
||||
},
|
||||
"redis-errors": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"eslint": "8.28.0",
|
||||
"eslint-config-next": "13.0.4",
|
||||
"framer-motion": "^7.6.19",
|
||||
"ioredis": "^4.28.5",
|
||||
"ioredis": "^5.2.5",
|
||||
"next": "13.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createRedisInstance } from "../../../misc/redis";
|
||||
import { getChannelEmotes, getGlobalEmotes } from "../../../misc/7TVAPI";
|
||||
|
||||
type Data = {
|
||||
|
@ -9,10 +10,19 @@ export default async function handler(
|
|||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
const channel = req.query.c
|
||||
? await getChannelEmotes(req.query.c as string)
|
||||
: undefined;
|
||||
const global = await getGlobalEmotes();
|
||||
const redis = createRedisInstance();
|
||||
|
||||
res.status(200).json({ channel, global });
|
||||
try {
|
||||
const channel = req.query.c
|
||||
? await getChannelEmotes(redis, req.query.c as string)
|
||||
: undefined;
|
||||
const global = await getGlobalEmotes(redis);
|
||||
redis.quit();
|
||||
res.status(200).json({ channel, global });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res
|
||||
.status(500)
|
||||
.json({ error: { message: "7TV or internal API is down", code: 10000 } });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ function Home() {
|
|||
fetch(api7tvEmotes)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
// if error, return
|
||||
if (data.error) {
|
||||
return;
|
||||
}
|
||||
// get all emote URLs
|
||||
let emoteUrls = data.channel.user.emote_sets[0].emotes.map(
|
||||
(emote: any) => {
|
||||
|
|
Loading…
Reference in a new issue