ENOBUFS error on Mongodb Nodejs

Asked

Viewed 60 times

1

My database connectorMongo for nodejs, after a while running triggers the ENOBUFS error, knocking down all the tcp connections from the computer.. other apps claim tcp exhaustion.. however I could not solve the problem, it seems after each query the application leaves the connection open.. To diagnose regarding the error source was made the "crash" of mongodb service and was reestablished the connections immediately.

below is the data

connection string

const mongoUrl = 'mongodb://localhost:27017/?readPreference=primary&appname=meuAplicativo&ssl=false&authSource=database01&keepAlive=false&poolSize=5&agent=false';

open connection to server

MongoClient.connect(mongoUrl, { "useUnifiedTopology": true, /*directConnection :true,*/ keepAlive: false, maxPoolSize:20},   async function(err, client) {
                        if(err) throw err
    
                        // entra na base de dados      
                        const database = client.db(mongoDBName);  
                        findAndModifyTags(__postBody['data'], database, 0, res, null);
                    });

Query

//seleciona coleção de dados
tagCollection = database.collection('values');

//procura e atualiza registro
tagCollection.updateOne(query, update,{returnNewDocument: false, upsert: true, new: true}, (error) => 
{
    if(!error) {
        console.log(`Successfully updated document`)
        //console.log(`Successfully updated document: ${JSON.stringify(doc)}.`)
    } else {
        console.log("No document matches the provided query.")
        console.log(error)
    }
    
    //resposta positiva    
    res.write("{'status': 'ok'}");
    res.end();
});  

Details of the Error

debug ->  2021-3-19 9:45:56 AM notnull
debug ->  2021-3-19 9:45:56 AM notnull
debug ->  2021-3-19 9:45:56 AM has data
(node:28792) UnhandledPromiseRejectionWarning: MongoServerSelectionError: connect ENOBUFS 127.0.0.1:27017 - Local (undefined:undefined)
    at Timeout.waitQueueMember.timer.setTimeout [as _onTimeout] (E:\Projetos\Telemetria Raspberry Zero\node_modules\mongodb\lib\core\sdam\topology.js:438:30)
    at ontimeout (timers.js:436:11)
    at tryOnTimeout (timers.js:300:5)
    at listOnTimeout (timers.js:263:5)
    at Timer.processTimers (timers.js:223:10)
(node:28792) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 357)

as can be seen, I have already made the pool reduction and some more procedures, but I still continue with the error

  • At what point is the connection created? Could add the code that contains the creation of the connection and at which time of the life cycle is made the communication with the database?

  • In fact it is already described above... in the first excerpt Mongoclient.connect, in the application lifecycle it basically receives a post request and adds to the database, requests are made every 10 seconds for each client, but per hour the test is with only one client it for after 2160 requests on average

  • I asked because in my mongodb applications I have only 1 connection per process. I never had this problem, maybe if I could see how it is structured my application will help.

  • in case you open the connection and leave it open for all requests? I’ll try, to see if it improves

  • If you want to have a project to be inspired you can see in my github the project tokenAuthMongo.

  • Man, I don’t know if it worked out, but I created an answer with the essential parts to a possible solution.

Show 1 more comment

1 answer

1


In this solution we make the connection with the mongo only at the time of npm start (an alias for node bin/www). Check into my package.json the section scritps.

After creating the connection we only use to make queries to the bank.

For a better understanding of the solution I use the architecture model defined by express-generator, para saber mais.

The directory structure:

.
├── app.js
├── bin
│   └── www
├── db
│   └── index.js
├── package.json
└── server
    |── routes
    |   └── tag.route.js
    ├── services
    |   └── tag.service.js
    └── index.js

In the archive db/index.js I make use of the lib mongodb, and export two functions connect() and get(). The function get will obtain the instance of our database, following the same idea of padrão de projeto Singleton.

db/index.js

const MongoClient = require('mongodb').MongoClient;
const url = `mongodb://${process.env.MONGOHOST}:${process.env.MONGOPORT}`
let dbinstance;

// Use connect method to connect to the server
connect = (callback) => {
    MongoClient.connect(url, { useUnifiedTopology: true }, (err, client) => {
        dbinstance = client.db(process.env.MONGONAME);
        callback (err);
    })
}

get = () => {
    return dbinstance;
}

module.exports = {
    connect,
    get
}

bin/www

#!/usr/bin/env node
const app = require('../app');
const dbinstance = require('../db/index')
dbinstance.connect(() => {
    app.listen(process.env.PORT);
    console.log(`Listening at http://localhost:${process.env.PORT}`);
});

In our service we only import our lib from the database(bd/index.js) and when consulting the bank use the method get() as can be seen from the following:

server/services/tag.service.js

const db = require('../../db/index');
const ObjectId = require('mongodb').ObjectID;

exports.updateTagbyId = (req, res) => {
    const options = {returnNewDocument: false, upsert: true, new: true}
    db.get().collection('tag').findOneAndUpdate({_id: ObjectId(req.params.id)}, {$set: {"name": req.body.name }}, options).then((result) => {
        if (result.value === null) {
            res.status(404).json({ msg: 'Não encontrado' })
            return
        }
        res.status(200).json({ status: 'ok' });
    }).catch((err) => {
        console.log(err)
        res.status(500).json({ msg: 'pff, houve um erro' })
    })
}

In this implementation I am using the res.json but it is possible to continue using the res.write the way you do in your code.

In this way we call the function connect() only in the bin/www in our service layer we are only using the connection through the method get().

package json.

//...
"scripts": {
    "dev": "nodemon ./bin/www",
    "start": "node ./bin/www"
},
"keywords": [
    "express",
    "mongodb"
],
//...

In my implementation I took the liberty of refactoring some parts, for example it is possible to see in your code that even if there is an error the return is 200. So in this my code there is the treatment for this case, in its code it uses the callback model to consult the mongodb, in my I use the promise/then/catch.

Some more recommendations that are not part of the code but that are needed:

  1. When I do the insertion in the database I include only the attributes that are part of my model. This will avoid adding unnecessary records and save a few cents of storage.
  2. Adding a record is only done after sanitizing and validating the data. This will prevent malicious entries and remote code execution.
  • perfect.. I’m running a test with this methodology to see if the error persists.. so far seems ok.. but I will wait to pass the amount of data in which the error was triggered

Browser other questions tagged

You are not signed in. Login or sign up in order to post.