PYTHON-920, PYTHON-921 - Fix metadata helpers with direct connection.

With this change metadata helpers (database_names, collection_names,
options, and index_information) no longer require a non-primary read
preference or the slave_okay option to run them against a directly
connected secondary or slave. This was the easiest way to make the
driver behave consistently across MongoDB versions and helpers. This
is also consistent with the server selection spec, though we are not
implementing that spec for PyMongo 2.x.
This commit is contained in:
Bernie Hackett 2015-05-06 09:14:55 -07:00
parent 4897c51090
commit 9bf46d2bb1
4 changed files with 41 additions and 7 deletions

View File

@ -1270,10 +1270,12 @@ class Collection(common.BaseObject):
client = self.database.connection
client._ensure_connected(True)
slave_okay = not client._rs_client and not client.is_mongos
if client.max_wire_version > 2:
res, addr = self.__database._command(
"listIndexes", self.__name, as_class=SON,
cursor={}, read_preference=ReadPreference.PRIMARY)
cursor={}, slave_okay=slave_okay,
read_preference=ReadPreference.PRIMARY)
# MongoDB 2.8rc2
if "indexes" in res:
raw = res["indexes"]
@ -1283,6 +1285,7 @@ class Collection(common.BaseObject):
else:
raw = self.__database.system.indexes.find({"ns": self.__full_name},
{"ns": 0}, as_class=SON,
slave_okay=slave_okay,
_must_use_master=True)
info = {}
for index in raw:
@ -1303,12 +1306,14 @@ class Collection(common.BaseObject):
client._ensure_connected(True)
result = None
slave_okay = not client._rs_client and not client.is_mongos
if client.max_wire_version > 2:
res, addr = self.__database._command(
"listCollections",
cursor={},
filter={"name": self.__name},
read_preference=ReadPreference.PRIMARY)
read_preference=ReadPreference.PRIMARY,
slave_okay=slave_okay)
# MongoDB 2.8rc2
if "collections" in res:
results = res["collections"]
@ -1320,7 +1325,9 @@ class Collection(common.BaseObject):
break
else:
result = self.__database.system.namespaces.find_one(
{"name": self.__full_name}, _must_use_master=True)
{"name": self.__full_name},
slave_okay=slave_okay,
_must_use_master=True)
if not result:
return {}

View File

@ -448,10 +448,12 @@ class Database(common.BaseObject):
client = self.connection
client._ensure_connected(True)
slave_okay = not client._rs_client and not client.is_mongos
if client.max_wire_version > 2:
res, addr = self._command("listCollections",
cursor={},
read_preference=ReadPreference.PRIMARY)
read_preference=ReadPreference.PRIMARY,
slave_okay=slave_okay)
# MongoDB 2.8rc2
if "collections" in res:
results = res["collections"]
@ -461,7 +463,9 @@ class Database(common.BaseObject):
names = [result["name"] for result in results]
else:
names = [result["name"] for result
in self["system.namespaces"].find(_must_use_master=True)]
in self["system.namespaces"].find(
slave_okay=slave_okay,
_must_use_master=True)]
names = [n[len(self.__name) + 1:] for n in names
if n.startswith(self.__name + ".") and "$" not in n]

View File

@ -1373,9 +1373,14 @@ class MongoClient(common.BaseObject):
def database_names(self):
"""Get a list of the names of all databases on the connected server.
"""
# SERVER-15994 changed listDatabases to require slaveOk when run
# against a secondary / slave. Passing slave_okay=True makes things
# consistent across server versions.
return [db["name"] for db in
self.admin.command("listDatabases",
read_preference=ReadPreference.PRIMARY)["databases"]]
self.admin.command(
"listDatabases",
read_preference=ReadPreference.PRIMARY,
slave_okay=not self.is_mongos)["databases"]]
def drop_database(self, name_or_database):
"""Drop a database.

View File

@ -82,6 +82,24 @@ class TestReadPreferencesBase(TestReplicaSetClientBase):
expected, used))
class TestSlaveOkayMetadataCommands(TestReadPreferencesBase):
def test_slave_okay_metadata_commands(self):
secondaries = iter(self._get_client().secondaries)
host, port = secondaries.next()
# Direct connection to a secondary.
client = MongoClient(host, port)
self.assertFalse(client.is_primary)
self.assertEqual(client.read_preference, ReadPreference.PRIMARY)
# No error.
client.database_names()
client.pymongo_test.collection_names()
client.pymongo_test.test.options()
client.pymongo_test.test.index_information()
class TestReadPreferences(TestReadPreferencesBase):
def test_mode_validation(self):
# 'modes' are imported from read_preferences.py