Problems using Railway's Redis Private Networking (Solved)

I have the following NestJs service:
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { Redis } from 'ioredis';
import { EnvService } from 'src/env/env.service';

@Injectable()
export class RedisService extends Redis implements OnModuleInit {
private readonly logger = new Logger(RedisService.name);
public readonly redisUrl = this.envService.get('REDIS_URL');
public readonly redisProtocol = this.envService.get('REDIS_PROTOCOL');

constructor(private readonly envService: EnvService) {
const redisUrl = envService.get('REDIS_URL');
const redisProtocol = envService.get('REDIS_PROTOCOL');

super(redisUrl, {
family: redisProtocol,
maxRetriesPerRequest: null,
connectTimeout: 30000, // 30 seconds
});

this.redisUrl = redisUrl;
this.redisProtocol = redisProtocol;

this.logger.log('creating redis client', redisUrl, redisProtocol);
}

async onModuleInit(): Promise<void> {
await new Promise<void>((resolve, reject) => {
this.logger.log('connecting to redis...');

this.on('connect', () => {
this.logger.log('connected to redis');
resolve();
});

this.on('error', (error) => {
this.logger.error('Error connecting to Redis:', error);
reject(error);
});
});
}
}
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { Redis } from 'ioredis';
import { EnvService } from 'src/env/env.service';

@Injectable()
export class RedisService extends Redis implements OnModuleInit {
private readonly logger = new Logger(RedisService.name);
public readonly redisUrl = this.envService.get('REDIS_URL');
public readonly redisProtocol = this.envService.get('REDIS_PROTOCOL');

constructor(private readonly envService: EnvService) {
const redisUrl = envService.get('REDIS_URL');
const redisProtocol = envService.get('REDIS_PROTOCOL');

super(redisUrl, {
family: redisProtocol,
maxRetriesPerRequest: null,
connectTimeout: 30000, // 30 seconds
});

this.redisUrl = redisUrl;
this.redisProtocol = redisProtocol;

this.logger.log('creating redis client', redisUrl, redisProtocol);
}

async onModuleInit(): Promise<void> {
await new Promise<void>((resolve, reject) => {
this.logger.log('connecting to redis...');

this.on('connect', () => {
this.logger.log('connected to redis');
resolve();
});

this.on('error', (error) => {
this.logger.error('Error connecting to Redis:', error);
reject(error);
});
});
}
}
In the Deploy Logs it prints the following values: [Nest] 25 - 02/27/2024, 2:52:04 PM LOG [RedisService] creating redis client [Nest] 25 - 02/27/2024, 2:52:04 PM LOG [RedisService] redis://default:[email protected]:6379 [Nest] 25 - 02/27/2024, 2:52:04 PM LOG [RedisService] 6 [Nest] 25 - 02/27/2024, 2:52:09 PM ERROR [RedisService] Error connecting to Redis: [Nest] 25 - 02/27/2024, 2:52:09 PM ERROR [RedisService] Error: getaddrinfo ENOTFOUND redis-eblx.railway.internal Any idea on what I am doing wrong?
29 Replies
Percy
Percy•9mo ago
Project ID: 03b9d68c-2d80-4600-a0a4-317a53176c97
!0xRafael 🔨👷
03b9d68c-2d80-4600-a0a4-317a53176c97
!0xRafael 🔨👷
I see this in the docs
ioredis is a Redis client for node.js, commonly used for connecting to Redis from a node application. When initializing a Redis client using ioredis, you must specify family=0 in the connection string to support connecting to both IPv6 and IPv4 connections: import Redis from 'ioredis'; const redis = new Redis(process.env.REDIS_PRIVATE_URL + '?family=0'); const ping = await redis.ping();
When I update my constructor to the following it unfortunately still doesn't work.
constructor(private readonly envService: EnvService) {
const redisProtocol = envService.get('REDIS_PROTOCOL');
const redisUrl = `${envService.get('REDIS_URL')}?family=${redisProtocol}`;

super(redisUrl, {
maxRetriesPerRequest: null,
connectTimeout: 30000, // 30 seconds
});

this.redisUrl = redisUrl;
this.redisProtocol = redisProtocol;

this.logger.log('creating redis client', redisUrl, redisProtocol);
}
constructor(private readonly envService: EnvService) {
const redisProtocol = envService.get('REDIS_PROTOCOL');
const redisUrl = `${envService.get('REDIS_URL')}?family=${redisProtocol}`;

super(redisUrl, {
maxRetriesPerRequest: null,
connectTimeout: 30000, // 30 seconds
});

this.redisUrl = redisUrl;
this.redisProtocol = redisProtocol;

this.logger.log('creating redis client', redisUrl, redisProtocol);
}
Logs: [Nest] 26 - 02/27/2024, 3:34:13 PM LOG [RedisService] creating redis client [Nest] 26 - 02/27/2024, 3:34:13 PM LOG [RedisService] redis://default:[email protected]:6379?family=0 [Nest] 26 - 02/27/2024, 3:34:13 PM LOG [RedisService] 0 [Nest] 25 - 02/27/2024, 3:34:41 PM ERROR [RedisService] Error connecting to Redis: [Nest] 25 - 02/27/2024, 3:34:41 PM ERROR [RedisService] Error: getaddrinfo ENOTFOUND redis-eblx.railway.internal With the public URL it works no problem Thanks for taking the time to respond.
Brody
Brody•9mo ago
looks like you are complicating things quite a bit, just try setting family to 0 in the options object please are you deploying with nixpacks?
!0xRafael 🔨👷
Looks like it
No description
Brody
Brody•9mo ago
okay cool, please try my suggestion
!0xRafael 🔨👷
When I do "const redisUrl = envService.get('REDIS_URL') + '?family=0';" it works, you right. Not sure why, it logs the same
Brody
Brody•9mo ago
awsome!
!0xRafael 🔨👷
Any idea why?
Brody
Brody•9mo ago
typo?
!0xRafael 🔨👷
When I use a text compare tool it is the same. No idea, I don't really have the time to look into it at the moment. Thanks a lot!
Brody
Brody•9mo ago
no problem!
!0xRafael 🔨👷
@Brody, nvm, seems like this one time was luck
Brody
Brody•9mo ago
okay can you add a 3 second sleep to your start script as to delay the starting of your app
!0xRafael 🔨👷
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { EnvService } from './env/env.service';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const envService = app.get(EnvService);
app.enableCors();
await app.listen(envService.get('PORT'));
}
bootstrap();
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { EnvService } from './env/env.service';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const envService = app.get(EnvService);
app.enableCors();
await app.listen(envService.get('PORT'));
}
bootstrap();
Before NestFactory.create or after?
Brody
Brody•9mo ago
please add the sleep in the start script, theres really no need to overcomplicate the solutions im trying to provide 🫤
!0xRafael 🔨👷
Ah, I think you mean here like this "start:prod": "sleep 3 && node dist/main" I am not over-complicating it on purpose. I am unsure what a "start script" is from your perspective. The package.json script, the code initializing the application, etc.
Brody
Brody•9mo ago
thats correct
!0xRafael 🔨👷
Seems like it is helping. Why?
No description
Brody
Brody•9mo ago
the private network's dns resolver takes about 3 seconds to start answering lookup requests, though its still only ipv6, so you do need family: 0 still
!0xRafael 🔨👷
Ok, thanks you, interesting. Even after multiple restarts it is working. I will increase it to 5 seconds just to be sure
Brody
Brody•9mo ago
i dont think theres a need for 5 seconds, over a 7 day period the slowest resolver ready time was only ~2.3 seconds
No description
!0xRafael 🔨👷
Interesting stats, very good to know. Now the private networking is solid. Thanks for the patience, I highly appreciate it :salute:
Brody
Brody•9mo ago
no problem! and for the record the team is working to remove any and all startup delays
!0xRafael 🔨👷
Great, can't wait, less unexpected stuff to worry about!
Brody
Brody•9mo ago
less help threads for me too, though it wont solve the need for the family 0 config thats something ioredis really should change, since it currently defaults to ipv4 only (family 4)
!0xRafael 🔨👷
Yeah, I had it on family 6, which did work once, but then after another redeploy it didn't work and I thought I had introduced a bug, but seems like the sleep was the only thing missing. Ipv6 generally still feels like a earlier adopter thing or like an afterthought, which is ridiculous
Duchess
Duchess•7mo ago
New reply sent from Help Station thread:
Just came across this same issue with ioredis and solved by setting family=0, thanks! Just for clarification to anyone else who finds this, the family option can take one of the following values: * 4: Use IPv4 protocol family. (default) * 6: Use IPv6 protocol family. * 0: Let the operating system decide which protocol family to use.
You're seeing this because this thread has been automatically linked to the Help Station thread.
Want results from more Discord servers?
Add your server