21

I have a bash shell script which does a bunch of stuff before trying to mongorestore.

I want to make sure that not only MongoDB is up, but it is also ready to accept connections before i try restore.

Right now, what I am seeing is, mongo process is up but it take 45+ seconds to do the initial setup (setting up journal files etc) before it is ready to accept connections. Ideally I want to keep testing the connection in a loop and only when I am able to connect, I want to run mongorestore.

Can someone show me how to do this in Bash or point me in the right direction?

1
  • When a mongod instance is ready is logs something like "waiting for connections on port 27017". You could just tail the log waiting for that output line and then start running mongo restore once that message has been logged. Not sure if this is the best solution, but it should work Commented Mar 15, 2013 at 23:05

7 Answers 7

30

To test the connection in a loop like you suggest,

until nc -z localhost 27017
do
    sleep 1
done
Sign up to request clarification or add additional context in comments.

6 Comments

tog...i tried your suggestion but that doesn't work. any other ideas?
Bad feedback: "It doesn't work". Good feedback: "I tried the commands verbatim, and it just waits forever. I checked stdout/stderr and it keeps printing bash: nc: command not found. How can I solve this?" or "I tried this, and it waits forever. However, the database is up and accepting connections, and netstat shows it listening: tcp 0 192.168.1.17:27017 0.0.0.0:* LISTEN. What could the problem be?" (in these cases, you'd install netcat or connect by lan ip instead of localhost respectively)
my apologies for a hasty reply. i tried the command verbatim and it proved to be an infinite loop. i.e. no matter what the state of the mongod process was, the loop kept running.
How about checking stderr and showing output from netstat -ln like in my examples?
In case of kubernetes deployment, and if you want to make sure that mongodb is ready before you start the application, based on your snippet, I added in my application deployment yaml : initContainers: - name: check-mongo-ready image: busybox:1.31 command: ['sh', '-c', 'until nc -z {{ .Values.mongo.dbhost }} {{ .Values.mongo.dbport }}; do echo waiting for mongodb; sleep 2; done;'] your application pod wont be created until mongodb port become accessible. I hope that helps people that googled this question for this specific need.
|
18

A solution using MongoDB Tools. Useful in a docker container or something similiar where you do not want to install nc.

until mongo --eval "print(\"waited for connection\")"
  do
    sleep 60
  done

Based on that other guy's answer.

1 Comment

For newer versions of mongo, use mongosh instead of mongo.
9

I recently had the same problem. I decided to configure mongod to log all it's output to a logfile and then wait in a loop checking the logfile until we see some output that suggests mongod is ready.

This is an example logfile output line we need to wait for:

Tue Dec  3 14:25:28.217 [initandlisten] waiting for connections on port 27017

This is the bash script I came up with:

#!/bin/bash

# Initialize a mongo data folder and logfile
mkdir -p /data/db
touch /var/log/mongodb.log

# Start mongodb with logging
# --logpath    Without this mongod will output all log information to the standard output.
# --logappend  Ensure mongod appends new entries to the end of the logfile. We create it first so that the below tail always finds something
/usr/bin/mongod  --quiet --logpath /var/log/mongodb.log --logappend &

# Wait until mongo logs that it's ready (or timeout after 60s)
COUNTER=0
grep -q 'waiting for connections on port' /var/log/mongodb.log
while [[ $? -ne 0 && $COUNTER -lt 60 ]] ; do
    sleep 2
    let COUNTER+=2
    echo "Waiting for mongo to initialize... ($COUNTER seconds so far)"
    grep -q 'waiting for connections on port' /var/log/mongodb.log
done

# Now we know mongo is ready and can continue with other commands
...

Notice the script will not wait forever, it will timeout after 60s - you may or may not want that depending on your use case.

2 Comments

But you have to reset the mongo logs for it to work.
"Now we know mongo is ready" is not true if the script timed out.
7

I needed Mongo running in Docker to initialize before creating a user. I combined the answers of Tom and Björn. This is the script I am using:

#!/bin/bash

# Wait until Mongo is ready to accept connections, exit if this does not happen within 30 seconds
COUNTER=0
until mongo --host ${MONGO_HOST} --eval "printjson(db.serverStatus())"
do
  sleep 1
  COUNTER=$((COUNTER+1))
  if [[ ${COUNTER} -eq 30 ]]; then
    echo "MongoDB did not initialize within 30 seconds, exiting"
    exit 2
  fi
  echo "Waiting for MongoDB to initialize... ${COUNTER}/30"
done

# Connect to the MongoDB and execute the create users script
mongo ${FWRD_API_DB} /root/create-user.js --host ${MONGO_HOST} -u ${MONGO_ROOT_USER} -p ${MONGO_ROOT_PASSWORD} --authenticationDatabase admin

Comments

6

While Tom's answer will work quite well in most situations, it can fail if you are running your script with the -e flag. I mean you run your script with set -e at the very top. Why? Because Tom's answer relies on the exit status of the previous command, in this case grep -q which will fail if it does not find the required string, and therefore the entire script will fail. The -e flag documentation gives a clue on how to avoid this problem:

The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted with !.

So, one solution is to make the grep command part of the while condition. However, since he is running mongodb with the --logappend option the search string could appear as a result of a previous run. I combined that other guy answer with Tom's answer and it works really well:

# Wait until mongo logs that it's ready (or timeout after 60s)
COUNTER=0
while !(nc -z localhost 27017) && [[ $COUNTER -lt 60 ]] ; do
    sleep 2
    let COUNTER+=2
    echo "Waiting for mongo to initialize... ($COUNTER seconds so far)"
done

I find that using tomcat is the best solution because it actually tests if there is something listening.

Comments

1

This approach is using in bitnami mongodb helm chart, but requires installed mongodb shell client:

mongosh  --host {your_mongo_host} --port {your_mongo_port} --eval "db.adminCommand('ping')"

Comments

0

Possible Docker solution:

Given the docker_id for me it works reading the docker logs like:

until [ $(docker logs --tail all $docker_id | grep "waiting for connections on port" | wc -l) -gt 0 ]; do
    printf '.'
    sleep 1
done

then continue with any mongo-dependent task.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.