I am running a simple Django app on top of MongoDB, and recently upgraded to PyMongo 3.0.2 -- but this runs incredibly slowly. If I downgrade to PyMongo 2.8.1 or 2.7.2, it speeds back up again. This happens with both MongoDB 3 and 2.6, so I'm thinking that something fundamental has changed. Per the changelog, PyMongo 3 is actually supposed to speed up a lot, and I can't find any obvious change that would cause a slowdown in performance. I have found no related issues on SO or Google. This is on Django 1.6.4 and Python 2.7.5.
It's hard to put up a single code example of this, but we are using a single MongoDB instance (no sharding, no remote hosts), and in each of our methods that uses the mongo_client
, we call close()
at the end of the method. Is there some new connection re-opening behavior that might slow down the client, if we continuously close the connection? Example method below:
from pymongo import MongoClient
mongo_client = MongoClient()
collection = mongo_client[self._db_prefix + 'assessment']['Assessment']
if collection.find({'itemIds': str(item_id)}).count() != 0:
raise errors.IllegalState('this Item is being used in one or more Assessments')
collection = mongo_client[self._db_prefix + 'assessment']['Item']
item_map = collection.find_one({'_id': ObjectId(item_id.get_identifier())})
if item_map is None:
raise errors.NotFound()
objects.Item(item_map, db_prefix=self._db_prefix, runtime=self._runtime)._delete()
delete_result = collection.delete_one({'_id': ObjectId(item_id.get_identifier())})
if delete_result.deleted_count == 0:
raise errors.NotFound()
mongo_client.close()
Update 1:
As suggested, I created a dedicated load test with the timeit
library. Using PyMongo 3.0.2:
timeit.timeit('MongoClient()["test_blah"]["blah"].insert_one({"foo":"bar"})', number=10000, setup="from pymongo import MongoClient")
Actually throws an error:
File "~/Documents/virtual_environments/assessments/lib/python2.7/site-packages/pymongo/pool.py", line 58, in _raise_connection_failure
raise AutoReconnect(msg)
AutoReconnect: localhost:27017: [Errno 49] Can't assign requested address
I then downgrade to PyMongo 2.8.1:
pip install pymongo==2.8.1
And run the same command in a python shell:
timeit.timeit('MongoClient()["test_blah"]["blah"].insert({"foo":"bar"})', number=10000, setup="from pymongo import MongoClient")
8.372910976409912
This time it actually finishes... So it seems like the new insert_one
method does something different, where it isn't closing connections?
Update 2 (with solution):
Bernie's answer helped point us in the right direction, as well as this SO question. In addition to using a single MongoClient()
, our problem was that we were closing the connection at the end of each method. Example timeit
s below (both PyMongo 3.0.2):
>>> timeit.timeit('client["test_blah"]["blah"].insert_one({"foo":"bar"}); client.close()', number=10, setup="from pymongo import MongoClient; client=MongoClient()")
4.520946025848389
>>> timeit.timeit('client["test_blah"]["blah"].insert_one({"foo":"bar"})', number=10, setup="from pymongo import MongoClient; client=MongoClient()")
0.004940986633300781
Manually closing the client is a performance killer...1000x slower. Perhaps caused by the slow monitor thread closing, that Bernie mentioned?