You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.7 KiB
130 lines
3.7 KiB
import { Redis } from 'ioredis'; |
|
|
|
import { parseIntFromEnvValue } from './utils.js'; |
|
|
|
/** |
|
* @typedef RedisConfiguration |
|
* @property {string|undefined} url |
|
* @property {import('ioredis').RedisOptions} options |
|
* @property {string|undefined} namespace |
|
*/ |
|
|
|
/** |
|
* |
|
* @param {NodeJS.ProcessEnv} env |
|
* @returns {boolean} |
|
*/ |
|
function hasSentinelConfiguration(env) { |
|
return ( |
|
typeof env.REDIS_SENTINELS === 'string' && |
|
env.REDIS_SENTINELS.length > 0 && |
|
typeof env.REDIS_SENTINEL_MASTER === 'string' && |
|
env.REDIS_SENTINEL_MASTER.length > 0 |
|
); |
|
} |
|
|
|
/** |
|
* |
|
* @param {NodeJS.ProcessEnv} env |
|
* @param {import('ioredis').SentinelConnectionOptions} commonOptions |
|
* @returns {import('ioredis').SentinelConnectionOptions} |
|
*/ |
|
function getSentinelConfiguration(env, commonOptions) { |
|
const redisDatabase = parseIntFromEnvValue(env.REDIS_DB, 0, 'REDIS_DB'); |
|
const sentinelPort = parseIntFromEnvValue(env.REDIS_SENTINEL_PORT, 26379, 'REDIS_SENTINEL_PORT'); |
|
|
|
const sentinels = env.REDIS_SENTINELS.split(',').map((sentinel) => { |
|
const [host, port] = sentinel.split(':', 2); |
|
|
|
/** @type {import('ioredis').SentinelAddress} */ |
|
return { |
|
host: host, |
|
port: port ?? sentinelPort, |
|
// Force support for both IPv6 and IPv4, by default ioredis sets this to 4, |
|
// only allowing IPv4 connections: |
|
// https://github.com/redis/ioredis/issues/1576 |
|
family: 0 |
|
}; |
|
}); |
|
|
|
return { |
|
db: redisDatabase, |
|
name: env.REDIS_SENTINEL_MASTER, |
|
username: env.REDIS_USER, |
|
password: env.REDIS_PASSWORD, |
|
sentinelUsername: env.REDIS_SENTINEL_USERNAME ?? env.REDIS_USER, |
|
sentinelPassword: env.REDIS_SENTINEL_PASSWORD ?? env.REDIS_PASSWORD, |
|
sentinels, |
|
...commonOptions, |
|
}; |
|
} |
|
|
|
/** |
|
* @param {NodeJS.ProcessEnv} env the `process.env` value to read configuration from |
|
* @returns {RedisConfiguration} configuration for the Redis connection |
|
*/ |
|
export function configFromEnv(env) { |
|
// These options apply for both REDIS_URL based connections and connections |
|
// using the other REDIS_* environment variables: |
|
const commonOptions = { |
|
// Force support for both IPv6 and IPv4, by default ioredis sets this to 4, |
|
// only allowing IPv4 connections: |
|
// https://github.com/redis/ioredis/issues/1576 |
|
family: 0 |
|
// Note: we don't use auto-prefixing of keys since this doesn't apply to |
|
// subscribe/unsubscribe which have "channel" instead of "key" arguments |
|
}; |
|
|
|
// If we receive REDIS_URL, don't continue parsing any other REDIS_* |
|
// environment variables: |
|
if (typeof env.REDIS_URL === 'string' && env.REDIS_URL.length > 0) { |
|
return { |
|
url: env.REDIS_URL, |
|
options: commonOptions |
|
}; |
|
} |
|
|
|
// If we have configuration for Redis Sentinel mode, prefer that: |
|
if (hasSentinelConfiguration(env)) { |
|
return { |
|
options: getSentinelConfiguration(env, commonOptions) |
|
}; |
|
} |
|
|
|
// Finally, handle all the other REDIS_* environment variables: |
|
let redisPort = parseIntFromEnvValue(env.REDIS_PORT, 6379, 'REDIS_PORT'); |
|
let redisDatabase = parseIntFromEnvValue(env.REDIS_DB, 0, 'REDIS_DB'); |
|
|
|
/** @type {import('ioredis').RedisOptions} */ |
|
const options = { |
|
host: env.REDIS_HOST ?? '127.0.0.1', |
|
port: redisPort, |
|
db: redisDatabase, |
|
username: env.REDIS_USER, |
|
password: env.REDIS_PASSWORD, |
|
...commonOptions, |
|
}; |
|
|
|
return { |
|
options |
|
}; |
|
} |
|
|
|
/** |
|
* @param {RedisConfiguration} config |
|
* @param {import('pino').Logger} logger |
|
* @returns {Redis} |
|
*/ |
|
export function createClient({ url, options }, logger) { |
|
let client; |
|
|
|
if (typeof url === 'string') { |
|
client = new Redis(url, options); |
|
} else { |
|
client = new Redis(options); |
|
} |
|
|
|
client.on('error', (err) => logger.error({ err }, 'Redis Client Error!')); |
|
|
|
return client; |
|
}
|
|
|