How do you check if the client for a MongoDB instance is valid?
PythonMongodbPymongoPython Problem Overview
In particular, I am currently trying to check if a connection to a client is valid using the following function:
def mongodb_connect(client_uri):
try:
return pymongo.MongoClient(client_uri)
except pymongo.errors.ConnectionFailure:
print "Failed to connect to server {}".format(client_uri)
I then use this function like this:
def bucket_summary(self):
client_uri = "some_client_uri"
client = mongodb_connect(client_uri)
db = client[tenant_id]
ttb = db.timebucket.count() # If I use an invalid URI it hangs here
Is there a way to catch and throw an exception at the last line if an invalid URI is given? I initially thought that's what the ConnectionFailure was for (so this could be caught when connecting) but I was wrong.
If I run the program with an invalid URI, which fails to run, issuing a KeyboardInterrupt yields:
File "reportjob_status.py", line 58, in <module>
tester.summarize_timebuckets()
File "reportjob_status.py", line 43, in summarize_timebuckets
ttb = db.timebucket.count() #error
File "/Library/Python/2.7/site-packages/pymongo/collection.py", line 1023, in count
return self._count(cmd)
File "/Library/Python/2.7/site-packages/pymongo/collection.py", line 985, in _count
with self._socket_for_reads() as (sock_info, slave_ok):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/Library/Python/2.7/site-packages/pymongo/mongo_client.py", line 699, in _socket_for_reads
with self._get_socket(read_preference) as sock_info:
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/Library/Python/2.7/site-packages/pymongo/mongo_client.py", line 663, in _get_socket
server = self._get_topology().select_server(selector)
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 121, in select_server
address))
File "/Library/Python/2.7/site-packages/pymongo/topology.py", line 106, in select_servers
self._condition.wait(common.MIN_HEARTBEAT_INTERVAL)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 358, in wait
_sleep(delay)
Python Solutions
Solution 1 - Python
The serverSelectionTimeoutMS
keyword parameter of pymongo.mongo_client.MongoClient
controls how long the driver will try to connect to a server. The default value is 30s.
Set it to a very low value compatible with your typical connection time¹ to immediately report an error. You need to query the DB after that to trigger a connection attempt :
>>> maxSevSelDelay = 1 # Assume 1ms maximum server selection delay
>>> client = pymongo.MongoClient("someInvalidURIOrNonExistantHost",
serverSelectionTimeoutMS=maxSevSelDelay)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>> client.server_info()
This will raise pymongo.errors.ServerSelectionTimeoutError
.
¹ Apparently setting serverSelectionTimeoutMS
to 0
might even work in the particular case your server has very low latency (case of a "local" server with very light load for example)
It is up to you to catch that exception and to handle it properly. Something like that:
try:
client = pymongo.MongoClient("someInvalidURIOrNonExistantHost",
serverSelectionTimeoutMS=maxSevSelDelay)
client.server_info() # force connection on a request as the
# connect=True parameter of MongoClient seems
# to be useless here
except pymongo.errors.ServerSelectionTimeoutError as err:
# do whatever you need
print(err)
will display:
No servers found yet
Solution 2 - Python
Hi to find out that the connection is established or not you can do that :
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
client = MongoClient()
try:
# The ismaster command is cheap and does not require auth.
client.admin.command('ismaster')
except ConnectionFailure:
print("Server not available")
Solution 3 - Python
serverSelectionTimeoutMS
> This defines how long to block for server selection before throwing an > exception. The default is 30,000 (milliseconds). It MUST be > configurable at the client level. It MUST NOT be configurable at the > level of a database object, collection object, or at the level of an > individual query. > > This default value was chosen to be sufficient for a typical server > primary election to complete. As the server improves the speed of > elections, this number may be revised downward. > > Users that can tolerate long delays for server selection when the > topology is in flux can set this higher. Users that want to "fail > fast" when the topology is in flux can set this to a small number. > > A serverSelectionTimeoutMS of zero MAY have special meaning in some > drivers; zero's meaning is not defined in this spec, but all drivers > SHOULD document the meaning of zero.
# pymongo 3.5.1
from pymongo import MongoClient
from pymongo.errors import ServerSelectionTimeoutError
client = MongoClient("mongodb://localhost:27000/", serverSelectionTimeoutMS=10, connectTimeoutMS=20000)
try:
info = client.server_info() # Forces a call.
except ServerSelectionTimeoutError:
print("server is down.")
# If connection create a new one with serverSelectionTimeoutMS=30000
Solution 4 - Python
serverSelectionTimeoutMS
doesn't work for me (Python 2.7.12, MongoDB 3.6.1, pymongo 3.6.0). A. Jesse Jiryu Davis suggested in a GitHub issue that we attempt a socket-level connection first as a litmus test. This does the trick for me.
def throw_if_mongodb_is_unavailable(host, port):
import socket
sock = None
try:
sock = socket.create_connection(
(host, port),
timeout=1) # one second
except socket.error as err:
raise EnvironmentError(
"Can't connect to MongoDB at {host}:{port} because: {err}"
.format(**locals()))
finally:
if sock is not None:
sock.close()
# elsewhere...
HOST = 'localhost'
PORT = 27017
throw_if_mongodb_is_unavailable(HOST, PORT)
import pymongo
conn = pymongo.MongoClient(HOST, PORT)
print(conn.admin.command('ismaster'))
# etc.
There are plenty of problems this won't catch, but if the server isn't running or isn't reachable, this'll show you right away.
Solution 5 - Python
Can also be checked this way:
from pymongo import MongoClient
from pymongo.errors import OperationFailure
def check_mongo_connection(client_uri):
connection = MongoClient(client_uri)
try:
connection.database_names()
print('Data Base Connection Established........')
except OperationFailure as err:
print(f"Data Base Connection failed. Error: {err}")
check_mongo_connection(client_uri)