Search code examples
mysqlnode.jsdatabasedatabase-connectioneconnreset

NodeJS - MySQL server not working after some time


Basically I have my Node.js server working along with MySQL. When I work on my localhost everything's fine. The connection to my local DB (I'm using XAMPPP) is great and nothing breaks. The problem comes along when the server is hosted by a provider. The one I hired uses cPanel and everithing's great until some time passes, because I get this error:

events.js:377
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:209:20)
Emitted 'error' event on Connection instance at:
    at Connection._handleProtocolError (/home/adminis6/Artesofa/node_modules/mysql/lib/Connection.js:423:8)
    at Protocol.emit (events.js:400:28)
    at Protocol._delegateError (/home/adminis6/Artesofa/node_modules/mysql/lib/protocol/Protocol.js:398:10)
    at Protocol.handleNetworkError (/home/adminis6/Artesofa/node_modules/mysql/lib/protocol/Protocol.js:371:10)
    at Connection._handleNetworkError (/home/adminis6/Artesofa/node_modules/mysql/lib/Connection.js:418:18)
    at Socket.emit (events.js:400:28)
    at emitErrorNT (internal/streams/destroy.js:106:8)
    at emitErrorCloseNT (internal/streams/destroy.js:74:3)
    at processTicksAndRejections (internal/process/task_queues.js:82:21) {
  errno: -104,
  code: 'ECONNRESET',
  syscall: 'read',
  fatal: true
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node app.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/adminis6/.npm/_logs/2021-11-30T19_15_37_322Z-debug.log

I've been researching how to solve this problem and the only useful answer I got basically said that the DB connection was timming out, so all I had to do was to make a request on an interval and hope it won't break. So I wrote the following code in my app.js file:

const fetch = require("node-fetch");

setInterval(() => {
    fetch('sample-endpoint');
}, 30000);

Although this seemed to have solved my problem, it appeared over and over again (note that the server did last longer being up).

Later on, some people taught me about CRONS so I made the following CRON:

PATH=$PATH:$HOME/bin; export PATH; /usr/bin/pgrep "node" >/dev/null || (cd /home/adminis6/Artesofa/; pkill node; pkill npm; nohup npm start &)

And it does work, because it gets the server up, but it instantly crashes (literally right after the server initiates, even after the server connects to the DB successfully), and it logs the following:

> [email protected] start /home/adminis6/Artesofa
> node app.js

Server running on port 3100 
mysql connected
events.js:377
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:209:20)
Emitted 'error' event on Connection instance at:
    at Connection._handleProtocolError (/home/adminis6/Artesofa/node_modules/mysql/lib/Connection.js:423:8)
    at Protocol.emit (events.js:400:28)
    at Protocol._delegateError (/home/adminis6/Artesofa/node_modules/mysql/lib/protocol/Protocol.js:398:10)
    at Protocol.handleNetworkError (/home/adminis6/Artesofa/node_modules/mysql/lib/protocol/Protocol.js:371:10)
    at Connection._handleNetworkError (/home/adminis6/Artesofa/node_modules/mysql/lib/Connection.js:418:18)
    at Socket.emit (events.js:400:28)
    at emitErrorNT (internal/streams/destroy.js:106:8)
    at emitErrorCloseNT (internal/streams/destroy.js:74:3)
    at processTicksAndRejections (internal/process/task_queues.js:82:21) {
  errno: -104,
  code: 'ECONNRESET',
  syscall: 'read',
  fatal: true
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node app.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/adminis6/.npm/_logs/2021-11-30T20_14_02_182Z-debug.log

I don't know what else to try nor I have much more time, please help!

If you need it, here is my app.js:

/* ----------- Server initialization ----------- */
//Here are all the modudule's require

const app = express();

// Connect to mySQL DB
const db = require('./db/connectDB');

// Set server to listen on specified port
app.listen(process.env.PORT || '4000', () => {
    console.log(`Server running on port ${process.env.PORT} AAAAA`);
})

app.set('view engine', 'ejs');
app.use(express.static('public'));

app.set('views', [
    path.join(__dirname, 'views/adminSite/')
]);

/* ----------- Middleware ----------- */

app.use(express.urlencoded({ extended: true }));
app.use(helmet());
app.use(cookieParser());
app.use(morgan('tiny'));

/* ----------- Routes ----------- */

app.use('/api', apiRoutes);

setInterval(() => {
    fetch('https://administracionartesofa.com/api/sucursales');
}, 30000);

And, finally, here is my connectDB file:

const mysql = require('mysql');
const dotenv = require('dotenv').config();
const settings = process.env.ENV === 'dev' ? require('./devSettings.json') : require('./prodSettings.json');

let db;

const connectDatabase = () => {
    if (!db) {
        db = mysql.createConnection(settings);

        db.connect((err) => {
            if (err) {
                console.log('Database error');
                console.log(err);
                connectDatabase();
            } else {
                console.log('mysql connected');
            }
        })
    }
    return db;
}

module.exports = connectDatabase();

Solution

  • Use a mysql connection pool in your nodejs program. Your hosting provider's cheap and nasty shared MySql server has an aggressively short idle connection time limit. If you hold open a connection for too long the server slams it shut, so you get ECONNRESET.

    Why? Cybercreeps trying to break in to random servers on the internet for fun and profit. This slows them down a bit, hopefully.

    Connection pools cope with this behind the scenes if you

    1. set up a pool at app startup, and
    2. grab a connection from the pool when you need one, use it, and then return it to the pool.

    Or, you can skip the pooling and just close your connection when you're done using it, then open a new one when you need it again. That will work fine for a low-volume app, but it might cause some inefficient connection thrashing if your volume goes up. Pools are better.