diff --git a/doc/examples/requests.rst b/doc/examples/requests.rst index 08da05ab2..fd3ef9394 100644 --- a/doc/examples/requests.rst +++ b/doc/examples/requests.rst @@ -1,11 +1,19 @@ Requests ======== -PyMongo supports the idea of a *request*: a series of operations executed with -a single socket, which are guaranteed to be processed on the server in the same -order as they ran on the client. +The ``start_request`` method of :class:`~pymongo.mongo_client.MongoClient` +and :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient` is now +**deprecated** and will be removed in PyMongo 3.0. -Requests are not usually necessary with PyMongo. +PyMongo versions previous to 3.0 support the idea of a *request*: a series of +operations executed with a single socket. This feature intended to make +read-your-writes consistency more likely, even with unacknowledged writes. +(That is, operations with write concern ``w=0``.) + +However, mongos 2.6 doesn't support socket pinning by default, and `mongos 2.8 +doesn't support it at all`_, so requests provide no benefit with sharding. + +In any case, requests are no longer necessary with PyMongo. By default, the methods :meth:`~pymongo.collection.Collection.insert`, :meth:`~pymongo.collection.Collection.update`, :meth:`~pymongo.collection.Collection.save`, and @@ -14,97 +22,7 @@ acknowledgment from the server, so ordered execution is already guaranteed. You can be certain the next :meth:`~pymongo.collection.Collection.find` or :meth:`~pymongo.collection.Collection.count`, for example, is executed on the server after the writes complete. This is called "read-your-writes -consistency." +consistency." If your application requires this consistency, do not override +the default write concern with ``w=0``. -An example of when a request is necessary is if a series of documents are -inserted with ``w=0`` for performance reasons, and you want to query those -documents immediately afterward: With ``w=0`` the writes can queue up at the -server and might not be immediately visible in query results. Wrapping the -inserts and queries within -:meth:`~pymongo.mongo_client.MongoClient.start_request` and -:meth:`~pymongo.mongo_client.MongoClient.end_request` forces a query to be on -the same socket as the inserts, so the query won't execute until the inserts -are complete on the server side. - -Example -------- - -Let's consider a collection of web-analytics counters. We want to count the -number of page views our site has served for each combination of browser, -region, and OS, and then show the user the number of page views from his or her -region, *including* the user's own visit. We have three ways to do so reliably: - -1. Simply update the counters with an acknowledged write (the default), and -then ``find`` all counters for the visitor's region. This will ensure that the -``update`` completes before the ``find`` begins, but it comes with a performance -penalty that may be unacceptable for analytics. - -2. Create the :class:`~pymongo.mongo_client.MongoClient` with ``w=0`` and -``auto_start_request=True`` to do unacknowledged writes and ensure each thread -gets its own socket. - -3. Explicitly call :meth:`~pymongo.mongo_client.MongoClient.start_request`, -then do the unacknowledged updates and the queries within the request. This -third method looks like: - -.. testsetup:: - - from pymongo import MongoClient - client = MongoClient() - counts = client.requests_example.counts - counts.drop() - region, browser, os = 'US', 'Firefox', 'Mac OS X' - -.. doctest:: - - >>> client = MongoClient() - >>> counts = client.requests_example.counts - >>> region, browser, os = 'US', 'Firefox', 'Mac OS X' - >>> request = client.start_request() - >>> try: - ... counts.update( - ... {'region': region, 'browser': browser, 'os': os}, - ... {'$inc': {'n': 1 }}, - ... upsert=True, - ... w=0) # unacknowledged write - ... - ... # This always runs after update has completed: - ... count = sum([p['n'] for p in counts.find({'region': region})]) - ... finally: - ... request.end() - >>> print count - 1 - -Requests can also be used as context managers, with the `with statement -`_, which makes -the previous example more terse: - -.. doctest:: - - >>> client.in_request() - False - >>> with client.start_request(): - ... # MongoClient is now in request - ... counts.update( - ... {'region': region, 'browser': browser, 'os': os}, - ... {'$inc': {'n': 1 }}, - ... upsert=True, - ... safe=False) - ... print sum([p['n'] for p in counts.find({'region': region})]) - 2 - >>> client.in_request() # request automatically ended - False - -Requests And ``max_pool_size`` ------------------------------- - -A thread in a request retains exclusive access to a socket until its request -ends or the thread dies; thus, applications in which more than 100 threads are -in requests at once should disable the ``max_pool_size`` option:: - - client = MongoClient(host, port, max_pool_size=None) - -Failure to increase or disable ``max_pool_size`` in such an application can -leave threads forever waiting for sockets. - -See :ref:`connection-pooling` +.. _mongos 2.8 doesn't support it at all: https://jira.mongodb.org/browse/SERVER-12273 diff --git a/pymongo/connection.py b/pymongo/connection.py index fe2ab9f08..622c0944e 100644 --- a/pymongo/connection.py +++ b/pymongo/connection.py @@ -116,8 +116,7 @@ class Connection(MongoClient): keep-alive packets). - `auto_start_request`: If ``True`` (the default), each thread that accesses this Connection has a socket allocated to it for the - thread's lifetime. This ensures consistent reads, even if you read - after an unsafe write. + thread's lifetime, or until :meth:`end_request` is called. - `use_greenlets`: if ``True``, :meth:`start_request()` will ensure that the current greenlet uses the same socket for all operations until :meth:`end_request()`. Defaults to ``False``. diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 0dbe19c08..da96a8421 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -152,10 +152,7 @@ class MongoClient(common.BaseObject): - `socketKeepAlive`: (boolean) Whether to send periodic keep-alive packets on connected sockets. Defaults to ``False`` (do not send keep-alive packets). - - `auto_start_request`: If ``True``, each thread that accesses - this :class:`MongoClient` has a socket allocated to it for the - thread's lifetime. This ensures consistent reads, even if you - read after an unacknowledged write. Defaults to ``False``. + - `auto_start_request`: Deprecated. - `use_greenlets`: If ``True``, :meth:`start_request()` will ensure that the current greenlet uses the same socket for all operations until :meth:`end_request()`. Defaults to ``False``. @@ -604,8 +601,7 @@ class MongoClient(common.BaseObject): @property def auto_start_request(self): - """Is auto_start_request enabled? - """ + """**DEPRECATED** Is auto_start_request enabled?""" return self.__auto_start_request def get_document_class(self): @@ -1236,26 +1232,17 @@ class MongoClient(common.BaseObject): raise AutoReconnect(str(e)) def start_request(self): - """Ensure the current thread or greenlet always uses the same socket - until it calls :meth:`end_request`. This ensures consistent reads, - even if you read after an unacknowledged write. + """DEPRECATED: start_request will be removed in PyMongo 3.0. - In Python 2.6 and above, or in Python 2.5 with - "from __future__ import with_statement", :meth:`start_request` can be - used as a context manager: + When doing w=0 writes to MongoDB 2.4 or earlier, :meth:`start_request` + was sometimes useful to ensure the current thread always used the same + socket until it called :meth:`end_request`. This made consistent reads + more likely after an unacknowledged write. Requests are no longer + useful in modern MongoDB applications, see + `PYTHON-785 `_. - >>> client = pymongo.MongoClient(auto_start_request=False) - >>> db = client.test - >>> _id = db.test_collection.insert({}) - >>> with client.start_request(): - ... for i in range(100): - ... db.test_collection.update({'_id': _id}, {'$set': {'i':i}}) - ... - ... # Definitely read the document after the final update completes - ... print db.test_collection.find({'_id': _id}) - - If a thread or greenlet calls start_request multiple times, an equal - number of calls to :meth:`end_request` is required to end the request. + .. versionchanged:: 2.8 + Deprecated. .. versionchanged:: 2.4 Now counts the number of calls to start_request and doesn't end @@ -1270,17 +1257,17 @@ class MongoClient(common.BaseObject): return pool.Request(self) def in_request(self): - """True if this thread is in a request, meaning it has a socket - reserved for its exclusive use. + """**DEPRECATED**: True if this thread is in a request, meaning it has + a socket reserved for its exclusive use. """ member = self.__member # Don't try to connect if disconnected. return member and member.in_request() def end_request(self): - """Undo :meth:`start_request`. If :meth:`end_request` is called as many - times as :meth:`start_request`, the request is over and this thread's - connection returns to the pool. Extra calls to :meth:`end_request` have - no effect. + """**DEPRECATED**: Undo :meth:`start_request`. If :meth:`end_request` + is called as many times as :meth:`start_request`, the request is over + and this thread's connection returns to the pool. Extra calls to + :meth:`end_request` have no effect. Ending a request allows the :class:`~socket.socket` that has been reserved for this thread by :meth:`start_request` to be returned to diff --git a/pymongo/mongo_replica_set_client.py b/pymongo/mongo_replica_set_client.py index 22e8587b4..f9e754f11 100644 --- a/pymongo/mongo_replica_set_client.py +++ b/pymongo/mongo_replica_set_client.py @@ -503,17 +503,11 @@ class MongoReplicaSetClient(common.BaseObject): - `socketKeepAlive`: (boolean) Whether to send periodic keep-alive packets on connected sockets. Defaults to ``False`` (do not send keep-alive packets). - - `auto_start_request`: If ``True``, each thread that accesses - this :class:`MongoReplicaSetClient` has a socket allocated to it - for the thread's lifetime, for each member of the set. For - :class:`~pymongo.read_preferences.ReadPreference` PRIMARY, - auto_start_request=True ensures consistent reads, even if you read - after an unacknowledged write. For read preferences other than - PRIMARY, there are no consistency guarantees. Default to ``False``. + - `auto_start_request`: Deprecated. - `use_greenlets`: If ``True``, use a background Greenlet instead of - a background thread to monitor state of replica set. Additionally, - :meth:`start_request()` assigns a greenlet-local, rather than - thread-local, socket. Defaults to ``False``. + a background thread to monitor the state of the replica set. + Additionally, :meth:`start_request` assigns a greenlet-local, + rather than thread-local, socket. Defaults to ``False``. `use_greenlets` with :class:`MongoReplicaSetClient` requires `Gevent `_ to be installed. @@ -1013,8 +1007,7 @@ class MongoReplicaSetClient(common.BaseObject): @property def auto_start_request(self): - """Is auto_start_request enabled? - """ + """**DEPRECATED** Is auto_start_request enabled?""" return self.__auto_start_request def __simple_command(self, sock_info, dbname, spec): @@ -1371,10 +1364,9 @@ class MongoReplicaSetClient(common.BaseObject): primary, else ``True``. This method attempts to check the status of the primary with minimal - I/O. The current thread / greenlet retrieves a socket (its request - socket if it's in a request, or a random idle socket if it's not in a - request) from the primary's connection pool and checks whether calling - select_ on it raises an error. If there are currently no idle sockets, + I/O. The current thread / greenlet retrieves a socket from the + primary's connection pool and checks whether calling select_ on it + raises an error. If there are currently no idle sockets, :meth:`alive` attempts to connect a new socket. A more certain way to determine primary availability is to ping it:: @@ -1733,26 +1725,17 @@ class MongoReplicaSetClient(common.BaseObject): raise AutoReconnect(str(e)) def start_request(self): - """Ensure the current thread or greenlet always uses the same socket - until it calls :meth:`end_request`. For - :class:`~pymongo.read_preferences.ReadPreference` PRIMARY, - auto_start_request=True ensures consistent reads, even if you read - after an unacknowledged write. For read preferences other than PRIMARY, - there are no consistency guarantees. + """DEPRECATED: start_request will be removed in PyMongo 3.0. - In Python 2.6 and above, or in Python 2.5 with - "from __future__ import with_statement", :meth:`start_request` can be - used as a context manager: + When doing w=0 writes to MongoDB 2.4 or earlier, :meth:`start_request` + was sometimes useful to ensure the current thread always used the same + socket until it called :meth:`end_request`. This made consistent reads + more likely after an unacknowledged write. Requests are no longer + useful in modern MongoDB applications, see + `PYTHON-785 `_. - >>> client = pymongo.MongoReplicaSetClient() - >>> db = client.test - >>> _id = db.test_collection.insert({}) - >>> with client.start_request(): - ... for i in range(100): - ... db.test_collection.update({'_id': _id}, {'$set': {'i':i}}) - ... - ... # Definitely read the document after the final update completes - ... print db.test_collection.find({'_id': _id}) + .. versionchanged:: 2.8 + Deprecated. .. versionadded:: 2.2 The :class:`~pymongo.pool.Request` return value. @@ -1771,26 +1754,14 @@ class MongoReplicaSetClient(common.BaseObject): return pool.Request(self) def in_request(self): - """True if :meth:`start_request` has been called, but not - :meth:`end_request`, or if `auto_start_request` is True and + """**DEPRECATED**: True if :meth:`start_request` has been called, but + not :meth:`end_request`, or if `auto_start_request` is True and :meth:`end_request` has not been called in this thread or greenlet. """ return bool(self.__request_counter.get()) def end_request(self): - """Undo :meth:`start_request` and allow this thread's connections to - replica set members to return to the pool. - - Calling :meth:`end_request` allows the :class:`~socket.socket` that has - been reserved for this thread by :meth:`start_request` to be returned - to the pool. Other threads will then be able to re-use that - :class:`~socket.socket`. If your application uses many threads, or has - long-running threads that infrequently perform MongoDB operations, then - judicious use of this method can lead to performance gains. Care should - be taken, however, to make sure that :meth:`end_request` is not called - in the middle of a sequence of operations in which ordering is - important. This could lead to unexpected results. - """ + """**DEPRECATED**: Undo :meth:`start_request`.""" rs_state = self.__rs_state if 0 == self.__request_counter.dec(): for member in rs_state.members: diff --git a/pymongo/replica_set_connection.py b/pymongo/replica_set_connection.py index 088b4ff71..3fa5a5f63 100644 --- a/pymongo/replica_set_connection.py +++ b/pymongo/replica_set_connection.py @@ -123,11 +123,8 @@ class ReplicaSetConnection(MongoReplicaSetClient): keep-alive packets). - `auto_start_request`: If ``True`` (the default), each thread that accesses this :class:`ReplicaSetConnection` has a socket allocated - to it for the thread's lifetime, for each member of the set. For - :class:`~pymongo.read_preferences.ReadPreference` PRIMARY, - auto_start_request=True ensures consistent reads, even if you read - after an unsafe write. For read preferences other than PRIMARY, - there are no consistency guarantees. + to it for each member of the set until the thread calls + :meth:`end_request` or terminates. - `use_greenlets`: if ``True``, use a background Greenlet instead of a background thread to monitor state of replica set. Additionally, :meth:`start_request()` will ensure that the current greenlet uses