R
Railway7mo ago
Yoginth

Getting [ioredis] Unhandled error event: ReplyError: ERR max number of clients reached

Any ideas why?
Solution:
youre trying to exceed the max clients allowable, this is a limitation of redis itself and not due to anything railway related. make sure to close connections when youre done with them. maybe look into how you could go about using pooling instead of a new client for every request....
Jump to solution
25 Replies
Percy
Percy7mo ago
Project ID: N/A
Yoginth
Yoginth7mo ago
No description
Brody
Brody7mo ago
can i see your connection constructor please?
Yoginth
Yoginth7mo ago
import { Redis } from 'ioredis';

const createRedisClient = () => {
return new Redis(process.env.REDIS_URL!, {
family: 6
});
};

export default createRedisClient;
import { Redis } from 'ioredis';

const createRedisClient = () => {
return new Redis(process.env.REDIS_URL!, {
family: 6
});
};

export default createRedisClient;
and we consume something like this
import { Errors } from '@hey/data/errors';
import logger from '@hey/lib/logger';
import catchedError from '@utils/catchedError';
import { SWR_CACHE_AGE_1_MIN_30_DAYS } from '@utils/constants';
import createRedisClient from '@utils/createRedisClient';
import prisma from '@utils/prisma';
import type { Handler } from 'express';

export const get: Handler = async (req, res) => {
const { id } = req.query;

if (!id) {
return res.status(400).json({ success: false, error: Errors.NoBody });
}

try {
const redis = createRedisClient();
const cache = await redis.get(`preferences:${id}`);

if (cache) {
logger.info('Profile preferences fetched from cache');
return res
.status(200)
.setHeader('Cache-Control', SWR_CACHE_AGE_1_MIN_30_DAYS)
.json({ success: true, cached: true, result: JSON.parse(cache) });
}

const [preference, pro, features] = await prisma.$transaction([
prisma.preference.findUnique({ where: { id: id as string } }),
prisma.pro.findFirst({ where: { profileId: id as string } }),
prisma.profileFeature.findMany({
where: {
profileId: id as string,
enabled: true,
feature: { enabled: true }
},
select: { feature: { select: { key: true } } }
})
]);

const response = {
preference,
pro: { enabled: Boolean(pro) },
features: features.map((feature: any) => feature.feature?.key)
};

await redis.set(`preferences:${id}`, JSON.stringify(response));
logger.info('Profile preferences fetched from DB');

return res
.status(200)
.setHeader('Cache-Control', SWR_CACHE_AGE_1_MIN_30_DAYS)
.json({
success: true,
result: response
});
} catch (error) {
return catchedError(res, error);
}
};
import { Errors } from '@hey/data/errors';
import logger from '@hey/lib/logger';
import catchedError from '@utils/catchedError';
import { SWR_CACHE_AGE_1_MIN_30_DAYS } from '@utils/constants';
import createRedisClient from '@utils/createRedisClient';
import prisma from '@utils/prisma';
import type { Handler } from 'express';

export const get: Handler = async (req, res) => {
const { id } = req.query;

if (!id) {
return res.status(400).json({ success: false, error: Errors.NoBody });
}

try {
const redis = createRedisClient();
const cache = await redis.get(`preferences:${id}`);

if (cache) {
logger.info('Profile preferences fetched from cache');
return res
.status(200)
.setHeader('Cache-Control', SWR_CACHE_AGE_1_MIN_30_DAYS)
.json({ success: true, cached: true, result: JSON.parse(cache) });
}

const [preference, pro, features] = await prisma.$transaction([
prisma.preference.findUnique({ where: { id: id as string } }),
prisma.pro.findFirst({ where: { profileId: id as string } }),
prisma.profileFeature.findMany({
where: {
profileId: id as string,
enabled: true,
feature: { enabled: true }
},
select: { feature: { select: { key: true } } }
})
]);

const response = {
preference,
pro: { enabled: Boolean(pro) },
features: features.map((feature: any) => feature.feature?.key)
};

await redis.set(`preferences:${id}`, JSON.stringify(response));
logger.info('Profile preferences fetched from DB');

return res
.status(200)
.setHeader('Cache-Control', SWR_CACHE_AGE_1_MIN_30_DAYS)
.json({
success: true,
result: response
});
} catch (error) {
return catchedError(res, error);
}
};
@Brody
Brody
Brody7mo ago
no need for the ping please
Yoginth
Yoginth7mo ago
here is the prod endpoint breaking, aka ~ millions of request broke from yesterday night https://api.hey.xyz/preference/getPreferences?id=0x01b69c it works fine with local redis
Brody
Brody7mo ago
i mean where you testing millions of requests locally too haha but what version of redis are you using locally?
Yoginth
Yoginth7mo ago
its 7.2.3 on local im not testing millions of req locally, but for like a week its fine on railway it broke suddenly even we didnt push any changes
Brody
Brody7mo ago
are you using a depricated redis plugin or a redis service on railway?
Yoginth
Yoginth7mo ago
no, not using any plugins its raw redis provided by railway
Yoginth
Yoginth7mo ago
imo its not with the code, i see same error on medis ui too
No description
Brody
Brody7mo ago
not quite what i asked, but i understand the plugin word i used can cause misinterpretation, so let me rephrase. does your redis database have a volume?
Yoginth
Yoginth7mo ago
yup yup lemme grab screenshot
Yoginth
Yoginth7mo ago
No description
Brody
Brody7mo ago
this is the max client count
No description
Yoginth
Yoginth7mo ago
No description
Solution
Brody
Brody7mo ago
youre trying to exceed the max clients allowable, this is a limitation of redis itself and not due to anything railway related. make sure to close connections when youre done with them. maybe look into how you could go about using pooling instead of a new client for every request.
Yoginth
Yoginth7mo ago
lemme check how to do that with ioredis
Brody
Brody7mo ago
side question, are you making sure to use the private url for redis? i do see the family:6 but im just making sure
Yoginth
Yoginth7mo ago
yes on railway project yes without family 6 private will not work
Brody
Brody7mo ago
indeed
Yoginth
Yoginth7mo ago
cuz railway handles with ipv6
Brody
Brody7mo ago
oh yes im no stranger to telling users to set family:6
Yoginth
Yoginth7mo ago
No description
Brody
Brody7mo ago
i usually tell them to set family:0 instead of 6 though, since thats both ipv6 and ipv4