Use node-redis for rate limiter (#18416)
* Use [node-]redis as a direct dependency * Extract Redis client creation to its own module * Attach extensive logging in the Redis client creation module * Allow the rate limiter to pass requests when Redis is disconnected * Update rate-limit-redis * Default error input to empty object for formatRedisError method * Provide a name for the rate limiter's Redis client Co-authored-by: Kevin Heis <heiskr@users.noreply.github.com> Co-authored-by: Rachael Sewell <rachmari@github.com>
This commit is contained in:
79
lib/redis/create-client.js
Normal file
79
lib/redis/create-client.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const Redis = require('redis')
|
||||
|
||||
const { REDIS_MIN_DB, REDIS_MAX_DB } = process.env
|
||||
|
||||
// By default, every Redis instance supports database numbers 0 - 15
|
||||
const redisMinDb = REDIS_MIN_DB || 0
|
||||
const redisMaxDb = REDIS_MAX_DB || 15
|
||||
|
||||
function formatRedisError (error = {}) {
|
||||
const { code } = error
|
||||
const preamble = error.constructor.name + (code ? ` with code "${code}"` : '')
|
||||
return preamble + error.toString()
|
||||
}
|
||||
|
||||
module.exports = function createClient (options = {}) {
|
||||
const { db, name, url } = options
|
||||
|
||||
// If no Redis URL is provided, bail out
|
||||
// NOTE: Could support other options like `host`, `port`, and `path` but
|
||||
// choosing not to for the time being!
|
||||
if (!url) return null
|
||||
|
||||
// Verify database number is within range
|
||||
if (db != null) {
|
||||
if (!Number.isInteger(db) || db < redisMinDb || db > redisMaxDb) {
|
||||
throw new TypeError(
|
||||
`Redis database number must be an integer between ${redisMinDb} and ${redisMaxDb} but was: ${JSON.stringify(db)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the client
|
||||
const client = Redis.createClient(url, {
|
||||
// Only add this configuration for TLS-enabled Redis URL values.
|
||||
// Otherwise, it breaks for local Redis instances without TLS enabled.
|
||||
...url.startsWith('rediss://') && {
|
||||
tls: {
|
||||
// Required for production Heroku Redis
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
},
|
||||
|
||||
// Expand whatever other options and overrides were provided
|
||||
...options
|
||||
})
|
||||
|
||||
// If a `name` was provided, use it in the prefix for logging event messages
|
||||
const logPrefix = '[redis' + (name ? ` (${name})` : '') + '] '
|
||||
|
||||
// Add event listeners for basic logging
|
||||
client.on('connect', () => { console.log(logPrefix, 'Connection opened') })
|
||||
client.on('ready', () => { console.log(logPrefix, 'Ready to receive commands') })
|
||||
client.on(
|
||||
'reconnecting',
|
||||
({
|
||||
attempt,
|
||||
delay,
|
||||
// The rest are unofficial properties but currently supported
|
||||
error,
|
||||
total_retry_time: totalRetryTime,
|
||||
times_connected: timesConnected
|
||||
}) => {
|
||||
console.log(
|
||||
logPrefix,
|
||||
'Reconnecting,',
|
||||
`attempt ${attempt}`,
|
||||
`with ${delay} delay`,
|
||||
`due to ${formatRedisError(error)}.`,
|
||||
`Elapsed time: ${totalRetryTime}.`,
|
||||
`Successful connections: ${timesConnected}.`
|
||||
)
|
||||
}
|
||||
)
|
||||
client.on('end', () => { console.log(logPrefix, 'Connection closed') })
|
||||
client.on('warning', (msg) => { console.warn(logPrefix, 'Warning:', msg) })
|
||||
client.on('error', (error) => { console.error(logPrefix, formatRedisError(error)) })
|
||||
|
||||
return client
|
||||
}
|
||||
Reference in New Issue
Block a user