replace redis client library with ioredis
This commit is contained in:
@ -27,9 +27,9 @@
|
||||
"@sentry/serverless": "^7.77.0",
|
||||
"axios": "^1.4.0",
|
||||
"dotenv": "^16.0.1",
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"linkedom": "^0.16.4",
|
||||
"redis": "^4.3.1",
|
||||
"rss-parser": "^3.13.0"
|
||||
},
|
||||
"volta": {
|
||||
|
||||
@ -2,17 +2,15 @@ import * as Sentry from '@sentry/serverless'
|
||||
import axios from 'axios'
|
||||
import crypto from 'crypto'
|
||||
import * as dotenv from 'dotenv' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
||||
import Redis from 'ioredis'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { parseHTML } from 'linkedom'
|
||||
import { createClient } from 'redis'
|
||||
import Parser, { Item } from 'rss-parser'
|
||||
import { promisify } from 'util'
|
||||
import { createRedisClient } from './redis'
|
||||
import { CONTENT_FETCH_URL, createCloudTask } from './task'
|
||||
|
||||
type FolderType = 'following' | 'inbox'
|
||||
// explicitly create the return type of RedisClient
|
||||
type RedisClient = ReturnType<typeof createClient>
|
||||
|
||||
interface RssFeedRequest {
|
||||
subscriptionIds: string[]
|
||||
@ -61,7 +59,7 @@ export const isOldItem = (item: RssFeedItem, lastFetchedAt: number) => {
|
||||
const feedFetchFailedRedisKey = (feedUrl: string) =>
|
||||
`feed-fetch-failure:${feedUrl}`
|
||||
|
||||
const isFeedBlocked = async (feedUrl: string, redisClient: RedisClient) => {
|
||||
const isFeedBlocked = async (feedUrl: string, redisClient: Redis) => {
|
||||
const key = feedFetchFailedRedisKey(feedUrl)
|
||||
try {
|
||||
const result = await redisClient.get(key)
|
||||
@ -78,10 +76,7 @@ const isFeedBlocked = async (feedUrl: string, redisClient: RedisClient) => {
|
||||
return false
|
||||
}
|
||||
|
||||
const incrementFeedFailure = async (
|
||||
feedUrl: string,
|
||||
redisClient: RedisClient
|
||||
) => {
|
||||
const incrementFeedFailure = async (feedUrl: string, redisClient: Redis) => {
|
||||
const key = feedFetchFailedRedisKey(feedUrl)
|
||||
try {
|
||||
const result = await redisClient.incr(key)
|
||||
@ -259,7 +254,7 @@ const sendUpdateSubscriptionMutation = async (
|
||||
}
|
||||
|
||||
const isItemRecentlySaved = async (
|
||||
redisClient: RedisClient,
|
||||
redisClient: Redis,
|
||||
userId: string,
|
||||
url: string
|
||||
) => {
|
||||
@ -274,7 +269,7 @@ const createTask = async (
|
||||
item: RssFeedItem,
|
||||
fetchContent: boolean,
|
||||
folder: FolderType,
|
||||
redisClient: RedisClient
|
||||
redisClient: Redis
|
||||
) => {
|
||||
const isRecentlySaved = await isItemRecentlySaved(
|
||||
redisClient,
|
||||
@ -464,7 +459,7 @@ const processSubscription = async (
|
||||
fetchContent: boolean,
|
||||
folder: FolderType,
|
||||
feed: RssFeed,
|
||||
redisClient: RedisClient
|
||||
redisClient: Redis
|
||||
) => {
|
||||
let lastItemFetchedAt: Date | null = null
|
||||
let lastValidItem: RssFeedItem | null = null
|
||||
@ -606,7 +601,7 @@ export const rssHandler = Sentry.GCPFunction.wrapHttpFunction(
|
||||
}
|
||||
|
||||
// create redis client
|
||||
const redisClient = await createRedisClient(
|
||||
const redisClient = createRedisClient(
|
||||
process.env.REDIS_URL,
|
||||
process.env.REDIS_CERT
|
||||
)
|
||||
|
||||
@ -1,26 +1,34 @@
|
||||
import { createClient } from 'redis'
|
||||
import { Redis } from 'ioredis'
|
||||
|
||||
export const createRedisClient = async (url?: string, cert?: string) => {
|
||||
const redisClient = createClient({
|
||||
url,
|
||||
socket: {
|
||||
tls: url?.startsWith('rediss://'), // rediss:// is the protocol for TLS
|
||||
cert: cert?.replace(/\\n/g, '\n'), // replace \n with new line
|
||||
rejectUnauthorized: false, // for self-signed certs
|
||||
connectTimeout: 10000, // 10 seconds
|
||||
reconnectStrategy(retries: number): number | Error {
|
||||
if (retries > 10) {
|
||||
return new Error('Retries exhausted')
|
||||
export const createRedisClient = (url?: string, cert?: string) => {
|
||||
return new Redis(url || 'redis://localhost:6379', {
|
||||
connectTimeout: 10000, // 10 seconds
|
||||
tls: cert
|
||||
? {
|
||||
cert,
|
||||
rejectUnauthorized: false, // for self-signed certs
|
||||
}
|
||||
return 1000
|
||||
},
|
||||
: undefined,
|
||||
reconnectOnError: (err) => {
|
||||
const targetErrors = [/READONLY/, /ETIMEDOUT/]
|
||||
|
||||
targetErrors.forEach((targetError) => {
|
||||
if (targetError.test(err.message)) {
|
||||
// Only reconnect when the error contains the keyword
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return false
|
||||
},
|
||||
retryStrategy: (times) => {
|
||||
if (times > 10) {
|
||||
// End reconnecting after a specific number of tries and flush all commands with a individual error
|
||||
return null
|
||||
}
|
||||
|
||||
// reconnect after
|
||||
return Math.min(times * 50, 2000)
|
||||
},
|
||||
})
|
||||
|
||||
redisClient.on('error', (err) => console.error('Redis Client Error', err))
|
||||
|
||||
await redisClient.connect()
|
||||
console.log('Redis Client Connected:', url)
|
||||
|
||||
return redisClient
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user