PYTHON-1280 - Support maxTimeMS in index management methods

This commit is contained in:
Bernie Hackett 2017-11-09 11:07:18 -08:00
parent 036188a832
commit 1227f5544d
5 changed files with 86 additions and 24 deletions

View File

@ -23,6 +23,12 @@ Highlights include:
:meth:`~pymongo.database.Database.list_collection_names`.
- Support for mongodb+srv:// URIs. See
:class:`~pymongo.mongo_client.MongoClient` for details.
- Index management helpers (
:meth:`~pymongo.collection.Collection.create_index`,
:meth:`~pymongo.collection.Collection.create_indexes`,
:meth:`~pymongo.collection.Collection.drop_index`,
:meth:`~pymongo.collection.Collection.drop_indexes`,
:meth:`~pymongo.collection.Collection.reindex`) now support maxTimeMS.
Deprecations:

View File

@ -1559,7 +1559,7 @@ class Collection(common.BaseObject):
cmd.update(kwargs)
return self._count(cmd, collation, session)
def create_indexes(self, indexes, session=None):
def create_indexes(self, indexes, session=None, **kwargs):
"""Create one or more indexes on this collection.
>>> from pymongo import IndexModel, ASCENDING, DESCENDING
@ -1574,6 +1574,8 @@ class Collection(common.BaseObject):
instances.
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
- `**kwargs` (optional): optional arguments to the createIndexes
command (like maxTimeMS) can be passed as keyword arguments.
.. note:: `create_indexes` uses the `createIndexes`_ command
introduced in MongoDB **2.6** and cannot be used with earlier
@ -1584,7 +1586,8 @@ class Collection(common.BaseObject):
MongoDB >= 3.4.
.. versionchanged:: 3.6
Added ``session`` parameter.
Added ``session`` parameter. Added support for arbitrary keyword
arguments.
.. versionchanged:: 3.4
Apply this collection's write concern automatically to this operation
@ -1595,17 +1598,24 @@ class Collection(common.BaseObject):
"""
common.validate_list('indexes', indexes)
names = []
def gen_indexes():
for index in indexes:
if not isinstance(index, IndexModel):
raise TypeError("%r is not an instance of "
"pymongo.operations.IndexModel" % (index,))
document = index.document
names.append(document["name"])
yield document
cmd = SON([('createIndexes', self.name),
('indexes', list(gen_indexes()))])
with self._socket_for_writes() as sock_info:
supports_collations = sock_info.max_wire_version >= 5
def gen_indexes():
for index in indexes:
if not isinstance(index, IndexModel):
raise TypeError(
"%r is not an instance of "
"pymongo.operations.IndexModel" % (index,))
document = index.document
if "collation" in document and not supports_collations:
raise ConfigurationError(
"Must be connected to MongoDB "
"3.4+ to use collations.")
names.append(document["name"])
yield document
cmd = SON([('createIndexes', self.name),
('indexes', list(gen_indexes()))])
cmd.update(kwargs)
self._command(
sock_info, cmd, read_preference=ReadPreference.PRIMARY,
codec_options=_UNICODE_REPLACE_CODEC_OPTIONS,
@ -1614,7 +1624,7 @@ class Collection(common.BaseObject):
session=session)
return names
def __create_index(self, keys, index_options, session):
def __create_index(self, keys, index_options, session, **kwargs):
"""Internal create index helper.
:Parameters:
@ -1637,6 +1647,7 @@ class Collection(common.BaseObject):
else:
index['collation'] = collation
cmd = SON([('createIndexes', self.name), ('indexes', [index])])
cmd.update(kwargs)
self._command(
sock_info, cmd, read_preference=ReadPreference.PRIMARY,
codec_options=_UNICODE_REPLACE_CODEC_OPTIONS,
@ -1721,7 +1732,8 @@ class Collection(common.BaseObject):
arguments
.. versionchanged:: 3.6
Added ``session`` parameter.
Added ``session`` parameter. Added support for passing maxTimeMS
in kwargs.
.. versionchanged:: 3.4
Apply this collection's write concern automatically to this operation
when connected to MongoDB >= 3.4. Support the `collation` option.
@ -1736,7 +1748,10 @@ class Collection(common.BaseObject):
"""
keys = helpers._index_list(keys)
name = kwargs.setdefault("name", helpers._gen_index_name(keys))
self.__create_index(keys, kwargs, session)
cmd_options = {}
if "maxTimeMS" in kwargs:
cmd_options["maxTimeMS"] = kwargs.pop("maxTimeMS")
self.__create_index(keys, kwargs, session, **cmd_options)
return name
def ensure_index(self, key_or_list, cache_for=300, **kwargs):
@ -1775,7 +1790,7 @@ class Collection(common.BaseObject):
return name
return None
def drop_indexes(self, session=None):
def drop_indexes(self, session=None, **kwargs):
"""Drops all indexes on this collection.
Can be used on non-existant collections or collections with no indexes.
@ -1784,13 +1799,16 @@ class Collection(common.BaseObject):
:Parameters:
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
- `**kwargs` (optional): optional arguments to the createIndexes
command (like maxTimeMS) can be passed as keyword arguments.
.. note:: The :attr:`~pymongo.collection.Collection.write_concern` of
this collection is automatically applied to this operation when using
MongoDB >= 3.4.
.. versionchanged:: 3.6
Added ``session`` parameter.
Added ``session`` parameter. Added support for arbitrary keyword
arguments.
.. versionchanged:: 3.4
Apply this collection's write concern automatically to this operation
@ -1798,9 +1816,9 @@ class Collection(common.BaseObject):
"""
self.__database.client._purge_index(self.__database.name, self.__name)
self.drop_index("*", session=session)
self.drop_index("*", session=session, **kwargs)
def drop_index(self, index_or_name, session=None):
def drop_index(self, index_or_name, session=None, **kwargs):
"""Drops the specified index on this collection.
Can be used on non-existant collections or collections with no
@ -1821,13 +1839,16 @@ class Collection(common.BaseObject):
- `index_or_name`: index (or name of index) to drop
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
- `**kwargs` (optional): optional arguments to the createIndexes
command (like maxTimeMS) can be passed as keyword arguments.
.. note:: The :attr:`~pymongo.collection.Collection.write_concern` of
this collection is automatically applied to this operation when using
MongoDB >= 3.4.
.. versionchanged:: 3.6
Added ``session`` parameter.
Added ``session`` parameter. Added support for arbitrary keyword
arguments.
.. versionchanged:: 3.4
Apply this collection's write concern automatically to this operation
@ -1844,6 +1865,7 @@ class Collection(common.BaseObject):
self.__database.client._purge_index(
self.__database.name, self.__name, name)
cmd = SON([("dropIndexes", self.__name), ("index", name)])
cmd.update(kwargs)
with self._socket_for_writes() as sock_info:
self._command(sock_info,
cmd,
@ -1853,19 +1875,22 @@ class Collection(common.BaseObject):
parse_write_concern_error=True,
session=session)
def reindex(self, session=None):
def reindex(self, session=None, **kwargs):
"""Rebuilds all indexes on this collection.
:Parameters:
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
- `**kwargs` (optional): optional arguments to the reIndex
command (like maxTimeMS) can be passed as keyword arguments.
.. warning:: reindex blocks all other operations (indexes
are built in the foreground) and will be slow for large
collections.
.. versionchanged:: 3.6
Added ``session`` parameter.
Added ``session`` parameter. Added support for arbitrary keyword
arguments.
.. versionchanged:: 3.4
Apply this collection's write concern automatically to this operation
@ -1877,6 +1902,7 @@ class Collection(common.BaseObject):
an error if we include the write concern.
"""
cmd = SON([("reIndex", self.__name)])
cmd.update(kwargs)
with self._socket_for_writes() as sock_info:
return self._command(
sock_info, cmd, read_preference=ReadPreference.PRIMARY,

View File

@ -351,7 +351,7 @@ class TestCollation(unittest.TestCase):
bulk.execute()
self.assertIsNone(self.db.test.find_one({'foo': 42}))
@client_context.require_version_min(3, 3, 11)
@raisesConfigurationErrorForOldMongoDB
def test_indexes_same_keys_different_collations(self):
self.db.test.drop()
usa_collation = Collation('en_US')

View File

@ -284,6 +284,35 @@ class TestCollection(IntegrationTest):
with self.write_concern_collection() as coll:
coll.drop_index('hello_1')
@client_context.require_no_mongos
@client_context.require_test_commands
def test_index_management_max_time_ms(self):
if (client_context.version[:2] == (3, 4) and
client_context.version[2] < 4):
raise unittest.SkipTest("SERVER-27711")
coll = self.db.test
self.client.admin.command("configureFailPoint",
"maxTimeAlwaysTimeOut",
mode="alwaysOn")
try:
self.assertRaises(
ExecutionTimeout, coll.create_index, "foo", maxTimeMS=1)
self.assertRaises(
ExecutionTimeout,
coll.create_indexes,
[IndexModel("foo")],
maxTimeMS=1)
self.assertRaises(
ExecutionTimeout, coll.drop_index, "foo", maxTimeMS=1)
self.assertRaises(
ExecutionTimeout, coll.drop_indexes, maxTimeMS=1)
self.assertRaises(
ExecutionTimeout, coll.reindex, maxTimeMS=1)
finally:
self.client.admin.command("configureFailPoint",
"maxTimeAlwaysTimeOut",
mode="off")
def test_reindex(self):
db = self.db
db.drop_collection("test")

View File

@ -169,7 +169,8 @@ class TestCursor(IntegrationTest):
self.assertTrue(coll.find_one(max_time_ms=1000))
client = self.client
if "enableTestCommands=1" in client_context.cmd_line['argv']:
if (not client_context.is_mongos
and client_context.test_commands_enabled):
# Cursor parses server timeout error in response to initial query.
client.admin.command("configureFailPoint",
"maxTimeAlwaysTimeOut",