I find this hard to believe it seems that when implementing a Redis backend cache in Apollo Server, if Apollo Server/Redis Client is unable to connect to the server, the Apollo thread is blocked until it is able to connect.
I've been digging through Apollo's server source code and it seems it probably relies on the Redis client itself?
This may be some configuration in Ioredis but I'm not able to find the right combination. Hoping someone can help.
When Apollo does its fetch through their HTTPCache class, this is the problematic code:
async fetch(
request: Request,
options: {
cacheKey?: string;
cacheOptions?:
| CacheOptions
| ((response: Response, request: Request) => CacheOptions | undefined);
} = {},
): Promise<Response> {
const cacheKey = options.cacheKey ? options.cacheKey : request.url;
const entry = await this.keyValueCache.get(cacheKey);
The very first thing it does is a get. And if that never resolves, it never continues on.
And my semi-random attempt at trying to find the right configuration for ioredis:
const cluster = new Redis.Cluster(
[
{
host: config.redis.endpoint,
port: config.redis.port,
},
],
{
retryDelayOnTryAgain: 0,
retryDelayOnClusterDown: 0,
retryDelayOnFailover: 0,
slotsRefreshTimeout: 0,
clusterRetryStrategy: (times, reason) => {
// Function should return how long to wait before retrying to connect to redis.
const maxRetryDelay = 30000;
const delay = Math.min(times * 1000, maxRetryDelay);
logger.info(`Redis`, `Connection retry. Try number ${times}. Delay: ${delay}`);
if (reason) logger.error(reason.message);
return delay; // Steadily increase retry times until max which is defined above.
},
redisOptions: {
tls: {
rejectUnauthorized: false,
},
autoResendUnfulfilledCommands: false,
retryStrategy: () => {
return;
},
disconnectTimeout: 0,
reconnectOnError: () => false,
connectTimeout: 0,
commandTimeout: 0,
maxRetriesPerRequest: 0,
connectionName: 'Tank Dev',
username: config.redis.auth.username,
password: config.redis.auth.password,
},
slotsRefreshInterval: 60000,
},
);