Socket already opened issue when using Redis cache along with Node.js
Asked Answered
C

2

11

I am facing, "Error: Socket already opened issue" when I am using Redis along with my node.js project.

I am trying to cache database results into Redis cache.. When Redis key is not empty, I will pick records from Redis Key. When its empty, I will pick from DB and set to Redis Key.

Here is my code:

const { response } = require('express');
var express = require('express');
var mysql = require('mysql');
const redis = require('redis');
const client = redis.createClient();

function GetLatestPosts() {
    return new Promise(async function(resolve, reject) {
        await client.connect();
        const value = await client.get('indexitems');
        if (value != null) {
            await client.disconnect();
            resolve(JSON.parse(value));
        }
        else {
            var PostsList;
            mysqldb.getConnection(function (err, connection) {
                var sql = "CALL PRC_GetPostsList()";
                connection.query(sql, async function (err, data, fields) {
                    if (err) throw err;
                    PostsList = data[0];
                    await client.set('indexitems', JSON.stringify(PostsList));
                    await client.expire('indexitems', 86400);
                    await client.disconnect();
                    resolve(PostsList);  
                });
            });
        }
    })
}

I am facing "Error: Socket already opened issue" randomly. Some times it works without any issue. Some times it shows Error: Socket already opened.

Please help me to resolve this issue. Thanks.

Here is my complete error:

Error: Socket already opened
RedisSocket.connect (/home/ubuntu/Projects/Site/Web/node_modules/@node-redis/client/dist/lib/client/socket.js:48:19)
Commander.connect (/home/ubuntu/Projects/Site/Web/node_modules/@node-redis/client/dist/lib/client/index.js:156:70)
/home/ubuntu/Projects/Site/Web/routes/index.js:224:22
new Promise (<anonymous>)
GetPostItems (/home/ubuntu/Projects/Site/Web/routes/index.js:223:12)
/home/ubuntu/Projects/Site/Web/routes/index.js:23:29
Layer.handle [as handle_request] (/home/ubuntu/Projects/Site/Web/node_modules/express/lib/router/layer.js:95:5)
next (/home/ubuntu/Projects/Site/Web/node_modules/express/lib/router/route.js:137:13)
Route.dispatch (/home/ubuntu/Projects/Site/Web/node_modules/express/lib/router/route.js:112:3)
Layer.handle [as handle_request] (/home/ubuntu/Projects/Site/Web/node_modules/express/lib/router/layer.js:95:5)
Cristie answered 21/12, 2021 at 15:54 Comment(0)
O
10

The problem occurs as client.connect() is called, whereas the redis client is already connected.

Whenever client.get('indexitems') returns a value, then the connection is correctly closed by await client.disconnect();

However, if there is no value, then an asynchronous call to mySQL is made, and the disconnection is only made in the callback of that request.

As this mySQL call happens asynchronously, the function GetLatestPosts may be executed again before the redis connection is closed, and client.connect() called a second time, provoking the error.

Solution

The connection to the redis client might be opened only once when the server starts, and kept opened.

This reduce the overhead of opening a new one at each request, then closing it.

The adapted code might then look like below:

const { response } = require('express');
var express = require('express');
var mysql = require('mysql');
const redis = require('redis');
const client = redis.createClient();

async function start() {

    await client.connect();

    function GetLatestPosts() {
        return new Promise(async function(resolve, reject) {
            const value = await client.get('indexitems');
            if (value != null) {
                resolve(JSON.parse(value));
            }
            else {
                var PostsList;
                mysqldb.getConnection(function (err, connection) {
                    var sql = "CALL PRC_GetPostsList()";
                    connection.query(sql, async function (err, data, fields) {
                        if (err) throw err;
                        PostsList = data[0];
                        await client.set('indexitems', JSON.stringify(PostsList));
                        await client.expire('indexitems', 86400);
                        resolve(PostsList);  
                    });
                });
            }
        })
    }
}

start()

Extra remarks:

  1. A graceful shutdown of the server might also be implemented, in order to close the connections to DB clients in a clean way.
  2. Same goes for the mysql connection: it can be opened only once at server startup.
  3. You might prefer to call client.quit() rather than client.disconnect(), in order to ensure that all commands get executed - as documented.
Obcordate answered 22/12, 2021 at 11:8 Comment(0)
S
1

npm install ioredis works well for serverless applications rather than the native package.!

https://www.npmjs.com/package/ioredis

Shimmery answered 1/6, 2022 at 17:42 Comment(2)
why do you say serverless. What do you mean?Ferro
@ChristianMatthew because it automatically manages connections rather than having to setup it up each time with native redis module. Its worked great for me so farShimmery

© 2022 - 2024 — McMap. All rights reserved.