~ubuntu-branches/ubuntu/wily/pymongo/wily-proposed

« back to all changes in this revision

Viewing changes to pymongo/master_slave_connection.py

  • Committer: Package Import Robot
  • Author(s): Federico Ceratto
  • Date: 2015-04-26 22:43:13 UTC
  • mfrom: (24.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20150426224313-0hga2jphvf0rrmfe
Tags: 3.0.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009-2014 MongoDB, Inc.
2
 
#
3
 
# Licensed under the Apache License, Version 2.0 (the "License");
4
 
# you may not use this file except in compliance with the License.
5
 
# You may obtain a copy of the License at
6
 
#
7
 
# http://www.apache.org/licenses/LICENSE-2.0
8
 
#
9
 
# Unless required by applicable law or agreed to in writing, software
10
 
# distributed under the License is distributed on an "AS IS" BASIS,
11
 
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 
# See the License for the specific language governing permissions and
13
 
# limitations under the License.
14
 
 
15
 
"""Master-Slave connection to Mongo.
16
 
 
17
 
Performs all writes to Master instance and distributes reads among all
18
 
slaves. Reads are tried on each slave in turn until the read succeeds
19
 
or all slaves failed.
20
 
"""
21
 
 
22
 
from pymongo import helpers, thread_util
23
 
from pymongo import ReadPreference
24
 
from pymongo.common import BaseObject
25
 
from pymongo.mongo_client import MongoClient
26
 
from pymongo.database import Database
27
 
from pymongo.errors import AutoReconnect
28
 
 
29
 
 
30
 
class MasterSlaveConnection(BaseObject):
31
 
    """A master-slave connection to Mongo.
32
 
    """
33
 
 
34
 
    def __init__(self, master, slaves=[], document_class=dict, tz_aware=False):
35
 
        """Create a new Master-Slave connection.
36
 
 
37
 
        The resultant connection should be interacted with using the same
38
 
        mechanisms as a regular `MongoClient`. The `MongoClient` instances used
39
 
        to create this `MasterSlaveConnection` can themselves make use of
40
 
        connection pooling, etc. `MongoClient` instances used as slaves should
41
 
        be created with the read_preference option set to
42
 
        :attr:`~pymongo.read_preferences.ReadPreference.SECONDARY`. Write
43
 
        concerns are inherited from `master` and can be changed in this
44
 
        instance.
45
 
 
46
 
        Raises TypeError if `master` is not an instance of `MongoClient` or
47
 
        slaves is not a list of at least one `MongoClient` instances.
48
 
 
49
 
        :Parameters:
50
 
          - `master`: `MongoClient` instance for the writable Master
51
 
          - `slaves`: list of `MongoClient` instances for the
52
 
            read-only slaves
53
 
          - `document_class` (optional): default class to use for
54
 
            documents returned from queries on this connection
55
 
          - `tz_aware` (optional): if ``True``,
56
 
            :class:`~datetime.datetime` instances returned as values
57
 
            in a document by this :class:`MasterSlaveConnection` will be timezone
58
 
            aware (otherwise they will be naive)
59
 
        """
60
 
        if not isinstance(master, MongoClient):
61
 
            raise TypeError("master must be a MongoClient instance")
62
 
        if not isinstance(slaves, list) or len(slaves) == 0:
63
 
            raise TypeError("slaves must be a list of length >= 1")
64
 
 
65
 
        for slave in slaves:
66
 
            if not isinstance(slave, MongoClient):
67
 
                raise TypeError("slave %r is not an instance of MongoClient" %
68
 
                                slave)
69
 
 
70
 
        super(MasterSlaveConnection,
71
 
              self).__init__(read_preference=ReadPreference.SECONDARY,
72
 
                             safe=master.safe,
73
 
                             **master.write_concern)
74
 
 
75
 
        self.__master = master
76
 
        self.__slaves = slaves
77
 
        self.__document_class = document_class
78
 
        self.__tz_aware = tz_aware
79
 
        self.__request_counter = thread_util.Counter(master.use_greenlets)
80
 
 
81
 
    @property
82
 
    def master(self):
83
 
        return self.__master
84
 
 
85
 
    @property
86
 
    def slaves(self):
87
 
        return self.__slaves
88
 
 
89
 
    @property
90
 
    def is_mongos(self):
91
 
        """If this MasterSlaveConnection is connected to mongos (always False)
92
 
 
93
 
        .. versionadded:: 2.3
94
 
        """
95
 
        return False
96
 
 
97
 
    @property
98
 
    def use_greenlets(self):
99
 
        """Whether calling :meth:`start_request` assigns greenlet-local,
100
 
        rather than thread-local, sockets.
101
 
 
102
 
        .. versionadded:: 2.4.2
103
 
        """
104
 
        return self.master.use_greenlets
105
 
 
106
 
    def get_document_class(self):
107
 
        return self.__document_class
108
 
 
109
 
    def set_document_class(self, klass):
110
 
        self.__document_class = klass
111
 
 
112
 
    document_class = property(get_document_class, set_document_class,
113
 
                              doc="""Default class to use for documents
114
 
                              returned on this connection.""")
115
 
 
116
 
    @property
117
 
    def tz_aware(self):
118
 
        return self.__tz_aware
119
 
 
120
 
    @property
121
 
    def max_bson_size(self):
122
 
        """Return the maximum size BSON object the connected master
123
 
        accepts in bytes. Defaults to 4MB in server < 1.7.4.
124
 
 
125
 
        .. versionadded:: 2.6
126
 
        """
127
 
        return self.master.max_bson_size
128
 
 
129
 
    @property
130
 
    def max_message_size(self):
131
 
        """Return the maximum message size the connected master
132
 
        accepts in bytes.
133
 
 
134
 
        .. versionadded:: 2.6
135
 
        """
136
 
        return self.master.max_message_size
137
 
 
138
 
    @property
139
 
    def min_wire_version(self):
140
 
        """The minWireVersion reported by the server.
141
 
 
142
 
        Returns ``0`` when connected to server versions prior to MongoDB 2.6.
143
 
 
144
 
        .. versionadded:: 2.7
145
 
        """
146
 
        return self.master.min_wire_version
147
 
 
148
 
    @property
149
 
    def max_wire_version(self):
150
 
        """The maxWireVersion reported by the server.
151
 
 
152
 
        Returns ``0`` when connected to server versions prior to MongoDB 2.6.
153
 
 
154
 
        .. versionadded:: 2.7
155
 
        """
156
 
        return self.master.max_wire_version
157
 
 
158
 
    @property
159
 
    def max_write_batch_size(self):
160
 
        """The maxWriteBatchSize reported by the server.
161
 
 
162
 
        Returns a default value when connected to server versions prior to
163
 
        MongoDB 2.6.
164
 
 
165
 
        .. versionadded:: 2.7
166
 
        """
167
 
        return self.master.max_write_batch_size
168
 
 
169
 
    def disconnect(self):
170
 
        """Disconnect from MongoDB.
171
 
 
172
 
        Disconnecting will call disconnect on all master and slave
173
 
        connections.
174
 
 
175
 
        .. seealso:: Module :mod:`~pymongo.mongo_client`
176
 
        .. versionadded:: 1.10.1
177
 
        """
178
 
        self.__master.disconnect()
179
 
        for slave in self.__slaves:
180
 
            slave.disconnect()
181
 
 
182
 
    def set_cursor_manager(self, manager_class):
183
 
        """Set the cursor manager for this connection.
184
 
 
185
 
        Helper to set cursor manager for each individual `MongoClient` instance
186
 
        that make up this `MasterSlaveConnection`.
187
 
        """
188
 
        self.__master.set_cursor_manager(manager_class)
189
 
        for slave in self.__slaves:
190
 
            slave.set_cursor_manager(manager_class)
191
 
 
192
 
    def _ensure_connected(self, sync):
193
 
        """Ensure the master is connected to a mongod/s.
194
 
        """
195
 
        self.__master._ensure_connected(sync)
196
 
 
197
 
    # _connection_to_use is a hack that we need to include to make sure
198
 
    # that killcursor operations can be sent to the same instance on which
199
 
    # the cursor actually resides...
200
 
    def _send_message(self, message,
201
 
                      with_last_error=False,
202
 
                      command=False, _connection_to_use=None):
203
 
        """Say something to Mongo.
204
 
 
205
 
        Sends a message on the Master connection. This is used for inserts,
206
 
        updates, and deletes.
207
 
 
208
 
        Raises ConnectionFailure if the message cannot be sent. Returns the
209
 
        request id of the sent message.
210
 
 
211
 
        :Parameters:
212
 
          - `operation`: opcode of the message
213
 
          - `data`: data to send
214
 
          - `safe`: perform a getLastError after sending the message
215
 
        """
216
 
        if _connection_to_use is None or _connection_to_use == -1:
217
 
            return self.__master._send_message(message,
218
 
                                               with_last_error, command)
219
 
        return self.__slaves[_connection_to_use]._send_message(
220
 
            message, with_last_error, command, check_primary=False)
221
 
 
222
 
    # _connection_to_use is a hack that we need to include to make sure
223
 
    # that getmore operations can be sent to the same instance on which
224
 
    # the cursor actually resides...
225
 
    def _send_message_with_response(self, message, _connection_to_use=None,
226
 
                                    _must_use_master=False, **kwargs):
227
 
        """Receive a message from Mongo.
228
 
 
229
 
        Sends the given message and returns a (connection_id, response) pair.
230
 
 
231
 
        :Parameters:
232
 
          - `operation`: opcode of the message to send
233
 
          - `data`: data to send
234
 
        """
235
 
        if _connection_to_use is not None:
236
 
            if _connection_to_use == -1:
237
 
                member = self.__master
238
 
                conn = -1
239
 
            else:
240
 
                member = self.__slaves[_connection_to_use]
241
 
                conn = _connection_to_use
242
 
            return (conn,
243
 
                    member._send_message_with_response(message, **kwargs)[1])
244
 
 
245
 
        # _must_use_master is set for commands, which must be sent to the
246
 
        # master instance. any queries in a request must be sent to the
247
 
        # master since that is where writes go.
248
 
        if _must_use_master or self.in_request():
249
 
            return (-1, self.__master._send_message_with_response(message,
250
 
                                                                  **kwargs)[1])
251
 
 
252
 
        # Iterate through the slaves randomly until we have success. Raise
253
 
        # reconnect if they all fail.
254
 
        for connection_id in helpers.shuffled(xrange(len(self.__slaves))):
255
 
            try:
256
 
                slave = self.__slaves[connection_id]
257
 
                return (connection_id,
258
 
                        slave._send_message_with_response(message,
259
 
                                                          **kwargs)[1])
260
 
            except AutoReconnect:
261
 
                pass
262
 
 
263
 
        raise AutoReconnect("failed to connect to slaves")
264
 
 
265
 
    def start_request(self):
266
 
        """Start a "request".
267
 
 
268
 
        Start a sequence of operations in which order matters. Note
269
 
        that all operations performed within a request will be sent
270
 
        using the Master connection.
271
 
        """
272
 
        self.__request_counter.inc()
273
 
        self.master.start_request()
274
 
 
275
 
    def in_request(self):
276
 
        return bool(self.__request_counter.get())
277
 
 
278
 
    def end_request(self):
279
 
        """End the current "request".
280
 
 
281
 
        See documentation for `MongoClient.end_request`.
282
 
        """
283
 
        self.__request_counter.dec()
284
 
        self.master.end_request()
285
 
 
286
 
    def __eq__(self, other):
287
 
        if isinstance(other, MasterSlaveConnection):
288
 
            us = (self.__master, self.slaves)
289
 
            them = (other.__master, other.__slaves)
290
 
            return us == them
291
 
        return NotImplemented
292
 
 
293
 
    def __ne__(self, other):
294
 
        return not self == other
295
 
 
296
 
    def __repr__(self):
297
 
        return "MasterSlaveConnection(%r, %r)" % (self.__master, self.__slaves)
298
 
 
299
 
    def __getattr__(self, name):
300
 
        """Get a database by name.
301
 
 
302
 
        Raises InvalidName if an invalid database name is used.
303
 
 
304
 
        :Parameters:
305
 
          - `name`: the name of the database to get
306
 
        """
307
 
        return Database(self, name)
308
 
 
309
 
    def __getitem__(self, name):
310
 
        """Get a database by name.
311
 
 
312
 
        Raises InvalidName if an invalid database name is used.
313
 
 
314
 
        :Parameters:
315
 
          - `name`: the name of the database to get
316
 
        """
317
 
        return self.__getattr__(name)
318
 
 
319
 
    def close_cursor(self, cursor_id, connection_id):
320
 
        """Close a single database cursor.
321
 
 
322
 
        Raises TypeError if cursor_id is not an instance of (int, long). What
323
 
        closing the cursor actually means depends on this connection's cursor
324
 
        manager.
325
 
 
326
 
        :Parameters:
327
 
          - `cursor_id`: cursor id to close
328
 
          - `connection_id`: id of the `MongoClient` instance where the cursor
329
 
            was opened
330
 
        """
331
 
        if connection_id == -1:
332
 
            return self.__master.close_cursor(cursor_id)
333
 
        return self.__slaves[connection_id].close_cursor(cursor_id)
334
 
 
335
 
    def database_names(self):
336
 
        """Get a list of all database names.
337
 
        """
338
 
        return self.__master.database_names()
339
 
 
340
 
    def drop_database(self, name_or_database):
341
 
        """Drop a database.
342
 
 
343
 
        :Parameters:
344
 
          - `name_or_database`: the name of a database to drop or the object
345
 
            itself
346
 
        """
347
 
        return self.__master.drop_database(name_or_database)
348
 
 
349
 
    def __iter__(self):
350
 
        return self
351
 
 
352
 
    def next(self):
353
 
        raise TypeError("'MasterSlaveConnection' object is not iterable")
354
 
 
355
 
    def _cached(self, database_name, collection_name, index_name):
356
 
        return self.__master._cached(database_name,
357
 
                                     collection_name, index_name)
358
 
 
359
 
    def _cache_index(self, database_name, collection_name,
360
 
                     index_name, cache_for):
361
 
        return self.__master._cache_index(database_name, collection_name,
362
 
                                          index_name, cache_for)
363
 
 
364
 
    def _purge_index(self, database_name,
365
 
                     collection_name=None, index_name=None):
366
 
        return self.__master._purge_index(database_name,
367
 
                                          collection_name,
368
 
                                          index_name)