1
0
mirror of synced 2025-12-22 03:16:52 -05:00
Files
docs/lib/redis/create-client.js
James M. Greene 84547e54c7 Use node-redis for page cache (#18421)
* 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

* Include redis-mock, exclude ioredis/ioredis-mock

* Remove unused RedisAccessor#exists method

* Switch RedisAccessor to use redis/redis-mock

* Provide a name for logging on the Redis page cache

* Remove extraneous trailing space from Redis logging prefix

Our updated use of console.* will already be adding a space after the prefix

* Replace ioredis-mock with redis-mock in tests

* Revert removal of ioredis dependency

* Bind Redis client to async promisified methods

* Extract former RedisAccessor constructor tests to new create-client tests

* Update RedisAccessor tests to work with the callback-based redis client

* Handle formatting Redis errors (or not) with more resiliency
2021-03-29 17:34:22 +00:00

82 lines
2.6 KiB
JavaScript

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 errorCode = error ? error.code : null
const errorName = error ? error.constructor.name : 'Error'
const errorMsg = error ? error.toString() : 'unknown error'
const preamble = errorName + (errorCode ? ` with code "${errorCode}"` : '')
return preamble + ': ' + errorMsg
}
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
}