sidorares / node-mysql2

:zap: fast mysqljs/mysql compatible mysql driver for node.js
https://sidorares.github.io/node-mysql2/
MIT License
4.07k stars 618 forks source link

Error: Can't add new command when connection is in closed state #939

Closed Rakeshvishnoi029 closed 2 years ago

Rakeshvishnoi029 commented 5 years ago

Hi

I fascing last 2 days this error please help me...

{ Error: read ETIMEDOUT at TCP.onread (net.js:622:25) errno: 'ETIMEDOUT', code: 'ETIMEDOUT', syscall: 'read', fatal: true } { Error: Can't add new command when connection is in closed state at PoolConnection._addCommandClosedState

I use mysql 2 and connect Pool

var mysql = require('mysql2'); var mysqlPool = mysql.createPool({
host: 'localhost', user: 'root', password: 'xyz', database: 'xyz', waitForConnections: true, connectionLimit: 10, queueLimit: 0 });

module.exports = mysqlPool;

55|| { Error: read ETIMEDOUT 55|| at TCP.onread (net.js:622:25) 55|| errno: 'ETIMEDOUT', 55|| code: 'ETIMEDOUT', 55| | syscall: 'read', 55|| fatal: true } 55|| { Error: Can't add new command when connection is in closed state 55| at PoolConnection._addCommandClosedState

ChromeUniverse commented 3 years ago

Thanks for the tips, @irepela and @MaksemM! I've finished refactoring my code to use connection pooling/pool queries with promises and deployed it to my server. Even so, I guess I'll still have to wait a couple of hours to see if it has any effect. I'll post an update if anything comes up.

ChromeUniverse commented 3 years ago

Update: it works!

Async/await connection pooling seems to have done the trick. For the first time, my server has been running for a whole day now without throwing any errors. If anyone's still having any issues, I suggest following irepela's and MaksemM's examples. My current setup is described below.

First, I'm creating a connection pool with mysql2/promise and exporting it for reuse across different modules/files.

// exporting MySQL connection pool object module.exports = mysql.createPool({ host: 'localhost', user: 'lucca', password: process.env.MYSQL_PASSWORD, database: 'tank_battle', waitForConnections: true, connectionLimit: 20, queueLimit: 0 })


Then, in my routes/middleware/APIs/whatever, I'm just importing the `pool` object and running the queries on the `pool` object, just as you would with a regular MySQL connection object. In other words, I'm using `await pool.query` instead of `await connection.execute` or `await connection.query`. 

* `api.js` - internal app logic for handling API requests:
```js
const { private } = require('../middleware/private');   // -> custom middleware for private routes
const pool = require('../sql_util');                    // -> creates connection pool (see above) 
const jwt = require("jsonwebtoken");                    // -> "jsonwebtoken" package from npm

// Express Router setup
const router = express.Router();
router.use(private);

// GET /user    --> returns user information
router.get('/user', async (req, res) => {

  const decoded = jwt.decode(req.token, {complete: true});
  const name = decoded.payload.username.toString();

  const sql = "select username, elo, lb_rank, tank_color from users where username=?"

  try {
    const [query_result, fields, err] = await pool.query(sql, [name]);

    if (query_result.length > 0) {

      const q = query_result[0]

      // BinaryRow {
      //   username: 'Lucca',
      //   elo: 1000,
      //   lb_rank: 1,
      //   tank_color: #12fca7
      // }

      const userData = {
        error: false,
        name: q.username,
        elo: q.elo,
        lb_rank: q.lb_rank,
        tank_color: q.tank_color
      }

      res.status(200);
      res.json(userData);
      return;

    } else {
      console.log('user not found');

      res.status(404);
      res.json({
        error: true,
        message: 'User not found'
      });

      return;
    }

  }

  catch (e) {
    res.status(500);
    res.json({
      error: true,
      message: 'Server Error'
    });
    console.log(e);
    return;
  }

});

module.exports = router;
ArthurMartiros commented 3 years ago

I have come across the same error and found out that i have: connection.release(); by mistake in my transactional code body before the actual end of the transaction. So the connection.release(); should be in the end of the execution as a normal release or in the rollback part as a failover release of the transaction because in transactional queries the separated several queries share same connection object. Whenever the participant queries or code snippets of the transactional query release the connection before the normal end of the transaction, they cause connection object to hang the socket connection by raising error prone behavior for consecutive queries which take part in transaction and only after the consecutive queries execution we will receive the mentioned error: Error: Can't add new command when connection is in closed state.

The above mentioned behavior is common for all type of queries that share same connection object. Thanks.

Nedudi commented 3 years ago

I am having the same issue (mysql 8 and knex.js with pool of connections)

multiwebinc commented 3 years ago

This is what I did to prevent this error from happening. I'm using async functions, so you may need to adapt this to your code, however, the important things to note are that you can check the _closing state of the connection to see if it is closed and you can hook onto the connection stream's close event to handle that:

const mysql = require('mysql2/promise');
const bluebird = require('bluebird');

let mysqlConnection = null;
const getMysqlConnection = async () => {
  // Check to see if connection exists and is not in the "closing" state
  if (!mysqlConnection || mysqlConnection?.connection?._closing) {
    mysqlConnection = await createNewMysqlConnection();
  }
  return mysqlConnection;
}

const createNewMysqlConnection = async () => {
  const connection = await mysql.createConnection({
    host: process.env.MYSQL_HOST,
    database: process.env.MYSQL_DATABASE,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    Promise: bluebird,
  });

  // You can do something here to handle the connection
  // being closed when it occurs.
  connection.connection.stream.on('close', () => {
    console.log("MySQL connection closed");
  });
  return connection;
}

Then in any functions that need access, just do

const connection = await getMysqlConnection();
OpakAlex commented 2 years ago

2 years, no solution, amazing why people still use this library?))))

ChromeUniverse commented 2 years ago

2 years, no solution, amazing why people still use this library?))))

Many people have already provided their own solutions on this issue thread, and most of them appear to work just fine. Could you at least read through a couple of them?

OpakAlex commented 2 years ago

yes, copy/paste the best solution, since 1950 ;) i think we did some progress at 2021, but looks like no))) Good i will copy paste code as all world.

ChromeUniverse commented 2 years ago

Alex

????????

\ Look, here's my point: no, this issue hasn't gone without any solutions for two years, people have already solved this problem before as detailed in previous comments (including the ones I made a couple of months ago) - but God forbid actually taking some time to read any of those, right?

Now, If you're so keen on figuring out stuff on your own, then by all means, feel free to keep banging your head on your keyboard while you sift through the meh-ish docs. Alternatively, you could save some time and frustration and take advantage of the work that the kind folks here have done for you by detailing their solutions to this, frankly, very dumb problem. Honestly, you won't be gaining much by not copying and pasting code from this thread.

Since this discussion is incredibly unproductive, I'll shut up now. Sorry. \

sidorares commented 2 years ago

While I don't have a good actual answer there are some useful proposals. If anyone wants to help to contribute there are some good ways do do so:

Closing the issue and locking comments, looks like discussion here does not add much value. =