0

I'm trying to dockerize Node.js application which connects to MongoDB using mongoose. It succeeds anytime I run node index.js from the shell when the connection URL is: mongodb://localhost:27017/deposit. If I restart my computer and then try to run the dockerized project (with mongo instead of localhost in url) with the command docker-compose up it fails to connect to MongoDB. But after I try again the same command, then it succeeds.

So my question is why node cannot connect to MongoDB on first try after the computer is restarted?

PS. Docker is running when I'm trying it

connection.js

const mongoose = require('mongoose');

const connection = "mongodb://mongo:27017/deposit";

const connectDb = () => {
    mongoose.connect(connection, {useNewUrlParser: true,  useUnifiedTopology: true}).then(res => console.log("Connected to DB"))
    .catch(err => console.log('>> Failed to connect to MongoDB, retrying...'));
};

module.exports = connectDb;

Dockerfile

 FROM node:latest

 RUN mkdir -p /app
 WORKDIR /app
 #/usr/src/app
 COPY package.json /app
 RUN npm install

 COPY . /app

 EXPOSE 7500

#  ENTRYPOINT ["node"]

 CMD ["node", "src/index.js"]

docker-compose.yml

version: "3"
services:
        deposit:
            container_name: deposit
            image: test/deposit
            restart: always
            build: .
            network_mode: host
            ports:
              - "7500:7500"
            depends_on:
              - mongo
        mongo:
              container_name: mongo
              image: mongo
              volumes:
                - /data:/data/db
              network_mode: host
              ports:
                - '27017:27017'
GiorgiSh
  • 127
  • 11
  • Possibly the mongo db is not ready to accept connections when you try to connect from the js app. I suggest that you implement a retry-connection mechanism. [More](https://mongoosejs.com/docs/connections.html#error-handling). – leopal Jun 23 '20 at 10:19

2 Answers2

0

In your case, node application starts before mongo being ready. There are two approaches to tackle this problem: To handle it in your docker-compose or your application. You can use wait-for-it.sh or write a wrapper script (both described here) to make sure that your node application starts after the db is ready. But as quoted from docker documentation, it is better to handle this in your application:

To handle this, design your application to attempt to re-establish a connection to the database after a failure. If the application retries the connection, it can eventually connect to the database.

The best solution is to perform this check in your application code, both at startup and whenever a connection is lost for any reason

You can implement mongo retry as below (Described in this answer):

var connectWithRetry = function() {
  return mongoose.connect(mongoUrl, function(err) {
    if (err) {
      console.error('Failed to connect to mongo on startup - retrying in 5 sec', err);
      setTimeout(connectWithRetry, 5000);
    }
  });
};
connectWithRetry();
Ana Lava
  • 769
  • 1
  • 6
  • 13
  • I tried it and the error stayed there. The error was the same before so I think mongo retries on its own if it can't connect. But one thing that seems not okay for me is that it only happens after a computer restart, if I do docker-compose up again, then it doesn't show error – GiorgiSh Jun 23 '20 at 14:30
0

As mentioned in the comment there might be the possibility the DB is not yet ready to accept the connection, so one way is to add retry logic or the other option is

serverSelectionTimeoutMS -

With useUnifiedTopology, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for serverSelectionTimeoutMS milliseconds. If not set, the MongoDB driver defaults to using 30000 (30 seconds).

So try with below option

const mongoose = require('mongoose');

const uri = 'mongodb://mongo:27017/deposit?retryWrites=true&w=majority';
mongoose.connect(uri, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  serverSelectionTimeoutMS: 50000
}).catch(err => console.log(err.reason));

But again if you init DB script getting bigger it will take more time, so you go with retry logic if that did not work. in the above script it will wait for 50 seconds.

Adiii
  • 54,482
  • 7
  • 145
  • 148
  • I tried it and the same error kept displaying. I think even before retrying logic was there. But one thing that seems not okay for me is that it only happens after a computer restart, if I do docker-compose up again, then it doesn't show error – GiorgiSh Jun 23 '20 at 14:29