Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js redis@4 Upgrade: SocketClosedUnexpectedlyError: Socket closed unexpectedly

Tags:

node.js

redis

I've got some legacy code that I'm upgrading from version 3 of the Node.js redis library to version 4 of the Node.js redis library. The basic shape of the code looks like this

var redis = require('redis')
var client = redis.createClient({
    port: '6379',
    host: process.env.REDIS_HOST,
    legacyMode: true
})
client.connect()
client.flushall(function (err, reply) {
    client.hkeys('hash key', function (err, replies) {
      console.log("key set done")
      client.quit()
    })
})
console.log("main done")

When I run this code with [email protected], I get the following error, and node.js exits with a non-zero status code

main done
key set done
events.js:292
      throw er; // Unhandled 'error' event
      ^

SocketClosedUnexpectedlyError: Socket closed unexpectedly
    at Socket.<anonymous> (/Users/astorm/Documents/redis4/node_modules/@redis/client/dist/lib/client/socket.js:182:118)
    at Object.onceWrapper (events.js:422:26)
    at Socket.emit (events.js:315:20)
    at TCP.<anonymous> (net.js:673:12)
Emitted 'error' event on Commander instance at:
    at RedisSocket.<anonymous> (/Users/astorm/Documents/redis4/node_modules/@redis/client/dist/lib/client/index.js:350:14)
    at RedisSocket.emit (events.js:315:20)
    at RedisSocket._RedisSocket_onSocketError (/Users/astorm/Documents/redis4/node_modules/@redis/client/dist/lib/client/socket.js:205:10)
    at Socket.<anonymous> (/Users/astorm/Documents/redis4/node_modules/@redis/client/dist/lib/client/socket.js:182:107)
    at Object.onceWrapper (events.js:422:26)
    at Socket.emit (events.js:315:20)
    at TCP.<anonymous> (net.js:673:12)
 

While in [email protected] it runs (minus the client.connect()) without issue.

I've been able to work around this by replacing client.quit() with client.disconnect(), but the actual code is a little more complex than the above example and I'd rather use the graceful shutdown of client.quit than the harsher "SHUT IT DOWN NOW" of client.disconnect().

Does anyone know what the issue here might be? Why is redis@4 failing with a SocketClosedUnexpectedlyError: Socket closed unexpectedly error.

like image 647
Alan Storm Avatar asked Dec 06 '25 17:12

Alan Storm


2 Answers

I have the same bug on a express js project,

  const redisClient = createClient({
    legacyMode: true,
    url: process.env.REDIS_URL,
  });

my error:

web.1 State changed from up to crashed
web.1 ERROR uncaughtException Socket closed unexpectedly
web.1 SocketClosedUnexpectedlyError: Socket closed unexpectedly

i change the code to

  const redisClient = createClient({
    legacyMode: true,
    url: process.env.REDIS_URL,
    pingInterval: 1000,
  });

the error disappear, i waited for an hour. So pingInterval helped me to keep socket alive

like image 197
Full-Stack Developer Avatar answered Dec 08 '25 06:12

Full-Stack Developer


What I found so far is that after a while (keepAlive default is 5 minutes) without any requests the Redis client closes and throws an error event, but if you don't handle this event it will crash your application. My solution for that was:

/* eslint-disable no-inline-comments */
import type { RedisClientType } from 'redis'
import { createClient } from 'redis'
import { config } from '@app/config'
import { logger } from '@app/utils/logger'

let redisClient: RedisClientType
let isReady: boolean

const cacheOptions = {
  url: config.redis.tlsFlag ? config.redis.urlTls : config.redis.url,
}

if (config.redis.tlsFlag) {
  Object.assign(cacheOptions, {
    socket: {
      // keepAlive: 300, // 5 minutes DEFAULT
      tls: false,
    },
  })
}

async function getCache(): Promise<RedisClientType> {
  if (!isReady) {
    redisClient = createClient({
      ...cacheOptions,
    })
    redisClient.on('error', err => logger.error(`Redis Error: ${err}`))
    redisClient.on('connect', () => logger.info('Redis connected'))
    redisClient.on('reconnecting', () => logger.info('Redis reconnecting'))
    redisClient.on('ready', () => {
      isReady = true
      logger.info('Redis ready!')
    })
    await redisClient.connect()
  }
  return redisClient
}

getCache().then(connection => {
  redisClient = connection
}).catch(err => {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  logger.error({ err }, 'Failed to connect to Redis')
})

export {
  getCache,
}

anyway... in your situation try to handle the error event

client.on('error', err => logger.error(`Redis Error: ${err}`))
like image 37
Russo Maromba Avatar answered Dec 08 '25 08:12

Russo Maromba



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!