PYTHON-1321 Remove MongoReplicaSetClient (#552)
This commit is contained in:
parent
6e8c3708b8
commit
56925fd97f
@ -9,10 +9,6 @@
|
||||
|
||||
Alias for :class:`pymongo.mongo_client.MongoClient`.
|
||||
|
||||
.. data:: MongoReplicaSetClient
|
||||
|
||||
Alias for :class:`pymongo.mongo_replica_set_client.MongoReplicaSetClient`.
|
||||
|
||||
.. data:: ReadPreference
|
||||
|
||||
Alias for :class:`pymongo.read_preferences.ReadPreference`.
|
||||
@ -46,7 +42,6 @@ Sub-modules:
|
||||
errors
|
||||
message
|
||||
mongo_client
|
||||
mongo_replica_set_client
|
||||
monitoring
|
||||
operations
|
||||
pool
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
:mod:`mongo_replica_set_client` -- Tools for connecting to a MongoDB replica set
|
||||
================================================================================
|
||||
|
||||
.. automodule:: pymongo.mongo_replica_set_client
|
||||
:synopsis: Tools for connecting to a MongoDB replica set
|
||||
|
||||
.. autoclass:: pymongo.mongo_replica_set_client.MongoReplicaSetClient(hosts_or_uri, document_class=dict, tz_aware=False, connect=True, **kwargs)
|
||||
|
||||
.. automethod:: close
|
||||
|
||||
.. describe:: c[db_name] || c.db_name
|
||||
|
||||
Get the `db_name` :class:`~pymongo.database.Database` on :class:`MongoReplicaSetClient` `c`.
|
||||
|
||||
Raises :class:`~pymongo.errors.InvalidName` if an invalid database name is used.
|
||||
|
||||
.. autoattribute:: primary
|
||||
.. autoattribute:: secondaries
|
||||
.. autoattribute:: arbiters
|
||||
.. autoattribute:: max_pool_size
|
||||
.. autoattribute:: max_bson_size
|
||||
.. autoattribute:: max_message_size
|
||||
.. autoattribute:: local_threshold_ms
|
||||
.. autoattribute:: codec_options
|
||||
.. autoattribute:: read_preference
|
||||
.. autoattribute:: write_concern
|
||||
.. automethod:: drop_database
|
||||
.. automethod:: get_database
|
||||
.. automethod:: close_cursor
|
||||
.. automethod:: kill_cursors
|
||||
.. automethod:: set_cursor_manager
|
||||
.. automethod:: get_default_database
|
||||
@ -25,6 +25,7 @@ Breaking Changes in 4.0
|
||||
- Removed :meth:`pymongo.database.Database.collection_names`.
|
||||
- Removed :meth:`pymongo.collection.Collection.parallel_scan`.
|
||||
- Removed :mod:`pymongo.thread_util`.
|
||||
- Removed :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`.
|
||||
|
||||
Notable improvements
|
||||
....................
|
||||
|
||||
@ -50,6 +50,15 @@ Warnings can also be changed to errors::
|
||||
.. note:: Not all deprecated features raise :exc:`DeprecationWarning` when
|
||||
used. See `Removed features with no migration path`_.
|
||||
|
||||
MongoReplicaSetClient
|
||||
---------------------
|
||||
|
||||
Removed :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`.
|
||||
Since PyMongo 3.0, ``MongoReplicaSetClient`` has been identical to
|
||||
:class:`pymongo.mongo_client.MongoClient`. Applications can simply replace
|
||||
``MongoReplicaSetClient`` with :class:`pymongo.mongo_client.MongoClient` and
|
||||
get the same behavior.
|
||||
|
||||
MongoClient
|
||||
-----------
|
||||
|
||||
|
||||
@ -89,7 +89,6 @@ from pymongo.common import (MIN_SUPPORTED_WIRE_VERSION,
|
||||
MAX_SUPPORTED_WIRE_VERSION)
|
||||
from pymongo.cursor import CursorType
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.operations import (IndexModel,
|
||||
InsertOne,
|
||||
DeleteOne,
|
||||
|
||||
@ -364,7 +364,7 @@ def validate_read_preference(dummy, value):
|
||||
|
||||
|
||||
def validate_read_preference_mode(dummy, value):
|
||||
"""Validate read preference mode for a MongoReplicaSetClient.
|
||||
"""Validate read preference mode for a MongoClient.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
Returns the original ``value`` instead of the validated read preference
|
||||
|
||||
@ -1001,8 +1001,7 @@ class MongoClient(common.BaseObject):
|
||||
`replicaSet` option.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
MongoClient gained this property in version 3.0 when
|
||||
MongoReplicaSetClient's functionality was merged in.
|
||||
MongoClient gained this property in version 3.0.
|
||||
"""
|
||||
return self._topology.get_primary()
|
||||
|
||||
@ -1015,8 +1014,7 @@ class MongoClient(common.BaseObject):
|
||||
client was created without the `replicaSet` option.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
MongoClient gained this property in version 3.0 when
|
||||
MongoReplicaSetClient's functionality was merged in.
|
||||
MongoClient gained this property in version 3.0.
|
||||
"""
|
||||
return self._topology.get_secondaries()
|
||||
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
# Copyright 2011-2015 MongoDB, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
||||
# may not use this file except in compliance with the License. You
|
||||
# may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# permissions and limitations under the License.
|
||||
|
||||
"""Deprecated. See :doc:`/examples/high_availability`."""
|
||||
|
||||
import warnings
|
||||
|
||||
from pymongo import mongo_client
|
||||
|
||||
|
||||
class MongoReplicaSetClient(mongo_client.MongoClient):
|
||||
"""Deprecated alias for :class:`~pymongo.mongo_client.MongoClient`.
|
||||
|
||||
:class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`
|
||||
will be removed in a future version of PyMongo.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
:class:`~pymongo.mongo_client.MongoClient` is now the one and only
|
||||
client class for a standalone server, mongos, or replica set.
|
||||
It includes the functionality that had been split into
|
||||
:class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`: it
|
||||
can connect to a replica set, discover all its members, and monitor
|
||||
the set for stepdowns, elections, and reconfigs.
|
||||
|
||||
The ``refresh`` method is removed from
|
||||
:class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`,
|
||||
as are the ``seeds`` and ``hosts`` properties.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn('MongoReplicaSetClient is deprecated, use MongoClient'
|
||||
' to connect to a replica set',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
||||
super(MongoReplicaSetClient, self).__init__(*args, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return "MongoReplicaSetClient(%s)" % (self._repr_helper(),)
|
||||
@ -38,7 +38,7 @@ _MONGOS_MODES = (
|
||||
|
||||
|
||||
def _validate_tag_sets(tag_sets):
|
||||
"""Validate tag sets for a MongoReplicaSetClient.
|
||||
"""Validate tag sets for a MongoClient.
|
||||
"""
|
||||
if tag_sets is None:
|
||||
return tag_sets
|
||||
@ -144,7 +144,7 @@ class _ServerMode(object):
|
||||
To specify a priority-order for tag sets, provide a list of
|
||||
tag sets: ``[{'dc': 'ny'}, {'dc': 'la'}, {}]``. A final, empty tag
|
||||
set, ``{}``, means "read from any member that matches the mode,
|
||||
ignoring tags." MongoReplicaSetClient tries each set of tags in turn
|
||||
ignoring tags." MongoClient tries each set of tags in turn
|
||||
until it finds a set of tags with at least one matching member.
|
||||
|
||||
.. seealso:: `Data-Center Awareness
|
||||
|
||||
@ -28,11 +28,10 @@ from bson.binary import Binary
|
||||
from bson.py3compat import StringIO, string_type
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.errors import (ConfigurationError,
|
||||
ConnectionFailure,
|
||||
NotMasterError,
|
||||
ServerSelectionTimeoutError)
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from gridfs.errors import CorruptGridFile, FileExists, NoFile
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test import (client_context,
|
||||
unittest,
|
||||
IntegrationTest)
|
||||
@ -487,7 +486,7 @@ class TestGridfs(IntegrationTest):
|
||||
self.assertIsNone(gout.md5)
|
||||
|
||||
|
||||
class TestGridfsReplicaSet(TestReplicaSetClientBase):
|
||||
class TestGridfsReplicaSet(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_secondaries_count(1)
|
||||
@ -500,7 +499,7 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase):
|
||||
|
||||
def test_gridfs_replica_set(self):
|
||||
rsc = rs_client(
|
||||
w=self.w,
|
||||
w=client_context.w,
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
|
||||
fs = gridfs.GridFS(rsc.gfsreplica, 'gfsreplicatest')
|
||||
@ -513,10 +512,7 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase):
|
||||
self.assertEqual(b'foo', content)
|
||||
|
||||
def test_gridfs_secondary(self):
|
||||
primary_host, primary_port = self.primary
|
||||
primary_connection = single_client(primary_host, primary_port)
|
||||
|
||||
secondary_host, secondary_port = one(self.secondaries)
|
||||
secondary_host, secondary_port = one(self.client.secondaries)
|
||||
secondary_connection = single_client(
|
||||
secondary_host, secondary_port,
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
@ -526,12 +522,12 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase):
|
||||
fs = gridfs.GridFS(secondary_connection.gfsreplica, 'gfssecondarytest')
|
||||
|
||||
# This won't detect secondary, raises error
|
||||
self.assertRaises(ConnectionFailure, fs.put, b'foo')
|
||||
self.assertRaises(NotMasterError, fs.put, b'foo')
|
||||
|
||||
def test_gridfs_secondary_lazy(self):
|
||||
# Should detect it's connected to secondary and not attempt to
|
||||
# create index.
|
||||
secondary_host, secondary_port = one(self.secondaries)
|
||||
secondary_host, secondary_port = one(self.client.secondaries)
|
||||
client = single_client(
|
||||
secondary_host,
|
||||
secondary_port,
|
||||
@ -543,7 +539,7 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase):
|
||||
|
||||
# Connects, doesn't create index.
|
||||
self.assertRaises(NoFile, fs.get_last_version)
|
||||
self.assertRaises(ConnectionFailure, fs.put, 'data')
|
||||
self.assertRaises(NotMasterError, fs.put, 'data')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -30,14 +30,13 @@ from bson.py3compat import StringIO, string_type
|
||||
from bson.son import SON
|
||||
from gridfs.errors import NoFile, CorruptGridFile
|
||||
from pymongo.errors import (ConfigurationError,
|
||||
ConnectionFailure,
|
||||
NotMasterError,
|
||||
ServerSelectionTimeoutError)
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from test import (client_context,
|
||||
unittest,
|
||||
IntegrationTest)
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test.utils import (ignore_deprecations,
|
||||
joinall,
|
||||
one,
|
||||
@ -504,7 +503,7 @@ class TestGridfs(IntegrationTest):
|
||||
self.assertIsNone(gout.md5)
|
||||
|
||||
|
||||
class TestGridfsBucketReplicaSet(TestReplicaSetClientBase):
|
||||
class TestGridfsBucketReplicaSet(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_secondaries_count(1)
|
||||
@ -517,7 +516,7 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase):
|
||||
|
||||
def test_gridfs_replica_set(self):
|
||||
rsc = rs_client(
|
||||
w=self.w,
|
||||
w=client_context.w,
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
|
||||
gfs = gridfs.GridFSBucket(rsc.gfsbucketreplica, 'gfsbucketreplicatest')
|
||||
@ -526,10 +525,7 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase):
|
||||
self.assertEqual(b'foo', content)
|
||||
|
||||
def test_gridfs_secondary(self):
|
||||
primary_host, primary_port = self.primary
|
||||
primary_connection = single_client(primary_host, primary_port)
|
||||
|
||||
secondary_host, secondary_port = one(self.secondaries)
|
||||
secondary_host, secondary_port = one(self.client.secondaries)
|
||||
secondary_connection = single_client(
|
||||
secondary_host, secondary_port,
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
@ -540,13 +536,13 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase):
|
||||
secondary_connection.gfsbucketreplica, 'gfsbucketsecondarytest')
|
||||
|
||||
# This won't detect secondary, raises error
|
||||
self.assertRaises(ConnectionFailure, gfs.upload_from_stream,
|
||||
self.assertRaises(NotMasterError, gfs.upload_from_stream,
|
||||
"test_filename", b'foo')
|
||||
|
||||
def test_gridfs_secondary_lazy(self):
|
||||
# Should detect it's connected to secondary and not attempt to
|
||||
# create index.
|
||||
secondary_host, secondary_port = one(self.secondaries)
|
||||
secondary_host, secondary_port = one(self.client.secondaries)
|
||||
client = single_client(
|
||||
secondary_host,
|
||||
secondary_port,
|
||||
@ -560,7 +556,7 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase):
|
||||
# Connects, doesn't create index.
|
||||
self.assertRaises(NoFile, gfs.open_download_stream_by_name,
|
||||
"test_filename")
|
||||
self.assertRaises(ConnectionFailure, gfs.upload_from_stream,
|
||||
self.assertRaises(NotMasterError, gfs.upload_from_stream,
|
||||
"test_filename", b'data')
|
||||
|
||||
|
||||
|
||||
@ -39,10 +39,10 @@ from pymongo.write_concern import WriteConcern
|
||||
|
||||
from test import (SkipTest,
|
||||
client_context,
|
||||
IntegrationTest,
|
||||
unittest,
|
||||
db_user,
|
||||
db_pwd)
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test.utils import (connected,
|
||||
ignore_deprecations,
|
||||
one,
|
||||
@ -87,7 +87,7 @@ class TestReadPreferenceObjects(unittest.TestCase):
|
||||
self.assertEqual(pref, copy.deepcopy(pref))
|
||||
|
||||
|
||||
class TestReadPreferencesBase(TestReplicaSetClientBase):
|
||||
class TestReadPreferencesBase(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_secondaries_count(1)
|
||||
@ -100,7 +100,7 @@ class TestReadPreferencesBase(TestReplicaSetClientBase):
|
||||
self.client.pymongo_test.test.drop()
|
||||
self.client.get_database(
|
||||
"pymongo_test",
|
||||
write_concern=WriteConcern(w=self.w)).test.insert_many(
|
||||
write_concern=WriteConcern(w=client_context.w)).test.insert_many(
|
||||
[{'_id': i} for i in range(10)])
|
||||
|
||||
self.addCleanup(self.client.pymongo_test.test.drop)
|
||||
@ -130,7 +130,7 @@ class TestReadPreferencesBase(TestReplicaSetClientBase):
|
||||
def assertReadsFrom(self, expected, **kwargs):
|
||||
c = rs_client(**kwargs)
|
||||
wait_until(
|
||||
lambda: len(c.nodes - c.arbiters) == self.w,
|
||||
lambda: len(c.nodes - c.arbiters) == client_context.w,
|
||||
"discovered all nodes")
|
||||
|
||||
used = self.read_from_which_kind(c)
|
||||
@ -284,7 +284,7 @@ class TestReadPreferences(TestReadPreferencesBase):
|
||||
read_preference=ReadPreference.NEAREST,
|
||||
localThresholdMS=10000) # 10 seconds
|
||||
|
||||
data_members = set(self.hosts).difference(set(self.arbiters))
|
||||
data_members = {self.client.primary} | self.client.secondaries
|
||||
|
||||
# This is a probabilistic test; track which members we've read from so
|
||||
# far, and keep reading until we've used all the members or give up.
|
||||
@ -347,7 +347,7 @@ _PREF_MAP = [
|
||||
]
|
||||
|
||||
|
||||
class TestCommandAndReadPreference(TestReplicaSetClientBase):
|
||||
class TestCommandAndReadPreference(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_secondaries_count(1)
|
||||
@ -355,7 +355,7 @@ class TestCommandAndReadPreference(TestReplicaSetClientBase):
|
||||
super(TestCommandAndReadPreference, cls).setUpClass()
|
||||
cls.c = ReadPrefTester(
|
||||
client_context.pair,
|
||||
replicaSet=cls.name,
|
||||
replicaSet=client_context.replica_set_name,
|
||||
# Ignore round trip times, to test ReadPreference modes only.
|
||||
localThresholdMS=1000*1000)
|
||||
if client_context.auth_enabled:
|
||||
@ -363,7 +363,7 @@ class TestCommandAndReadPreference(TestReplicaSetClientBase):
|
||||
cls.client_version = Version.from_client(cls.c)
|
||||
# mapReduce and group fail with no collection
|
||||
coll = cls.c.pymongo_test.get_collection(
|
||||
'test', write_concern=WriteConcern(w=cls.w))
|
||||
'test', write_concern=WriteConcern(w=client_context.w))
|
||||
coll.insert_one({})
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -1,376 +0,0 @@
|
||||
# Copyright 2011-present MongoDB, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Test the mongo_replica_set_client module."""
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
import time
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.codec_options import CodecOptions
|
||||
from bson.son import SON
|
||||
from pymongo.common import MAX_SUPPORTED_WIRE_VERSION, partition_node
|
||||
from pymongo.errors import (AutoReconnect,
|
||||
ConfigurationError,
|
||||
ConnectionFailure,
|
||||
NetworkTimeout,
|
||||
NotMasterError,
|
||||
OperationFailure)
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.read_preferences import ReadPreference, Secondary, Nearest
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import (client_context,
|
||||
client_knobs,
|
||||
IntegrationTest,
|
||||
unittest,
|
||||
SkipTest,
|
||||
db_pwd,
|
||||
db_user,
|
||||
MockClientTest,
|
||||
HAVE_IPADDRESS)
|
||||
from test.pymongo_mocks import MockClient
|
||||
from test.utils import (connected,
|
||||
delay,
|
||||
ignore_deprecations,
|
||||
one,
|
||||
rs_client,
|
||||
single_client,
|
||||
wait_until)
|
||||
|
||||
|
||||
class TestReplicaSetClientBase(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
@client_context.require_replica_set
|
||||
def setUpClass(cls):
|
||||
super(TestReplicaSetClientBase, cls).setUpClass()
|
||||
cls.name = client_context.replica_set_name
|
||||
cls.w = client_context.w
|
||||
|
||||
ismaster = client_context.ismaster
|
||||
cls.hosts = set(partition_node(h.lower()) for h in ismaster['hosts'])
|
||||
cls.arbiters = set(partition_node(h)
|
||||
for h in ismaster.get("arbiters", []))
|
||||
|
||||
repl_set_status = client_context.client.admin.command(
|
||||
'replSetGetStatus')
|
||||
primary_info = [
|
||||
m for m in repl_set_status['members']
|
||||
if m['stateStr'] == 'PRIMARY'
|
||||
][0]
|
||||
|
||||
cls.primary = partition_node(primary_info['name'].lower())
|
||||
cls.secondaries = set(
|
||||
partition_node(m['name'].lower())
|
||||
for m in repl_set_status['members']
|
||||
if m['stateStr'] == 'SECONDARY')
|
||||
|
||||
|
||||
class TestReplicaSetClient(TestReplicaSetClientBase):
|
||||
def test_deprecated(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", DeprecationWarning)
|
||||
with self.assertRaises(DeprecationWarning):
|
||||
MongoReplicaSetClient()
|
||||
|
||||
def test_connect(self):
|
||||
client = MongoClient(
|
||||
client_context.pair,
|
||||
replicaSet='fdlksjfdslkjfd',
|
||||
serverSelectionTimeoutMS=100)
|
||||
|
||||
with self.assertRaises(ConnectionFailure):
|
||||
client.test.test.find_one()
|
||||
|
||||
def test_repr(self):
|
||||
with ignore_deprecations():
|
||||
client = MongoReplicaSetClient(
|
||||
client_context.host,
|
||||
client_context.port,
|
||||
replicaSet=self.name)
|
||||
|
||||
self.assertIn("MongoReplicaSetClient(host=[", repr(client))
|
||||
self.assertIn(client_context.pair, repr(client))
|
||||
|
||||
def test_properties(self):
|
||||
c = client_context.client
|
||||
c.admin.command('ping')
|
||||
|
||||
wait_until(lambda: c.primary == self.primary, "discover primary")
|
||||
wait_until(lambda: c.arbiters == self.arbiters, "discover arbiters")
|
||||
wait_until(lambda: c.secondaries == self.secondaries,
|
||||
"discover secondaries")
|
||||
|
||||
self.assertEqual(c.primary, self.primary)
|
||||
self.assertEqual(c.secondaries, self.secondaries)
|
||||
self.assertEqual(c.arbiters, self.arbiters)
|
||||
self.assertEqual(c.max_pool_size, 100)
|
||||
|
||||
# Make sure MongoClient's properties are copied to Database and
|
||||
# Collection.
|
||||
for obj in c, c.pymongo_test, c.pymongo_test.test:
|
||||
self.assertEqual(obj.codec_options, CodecOptions())
|
||||
self.assertEqual(obj.read_preference, ReadPreference.PRIMARY)
|
||||
self.assertEqual(obj.write_concern, WriteConcern())
|
||||
|
||||
cursor = c.pymongo_test.test.find()
|
||||
self.assertEqual(
|
||||
ReadPreference.PRIMARY, cursor._read_preference())
|
||||
|
||||
tag_sets = [{'dc': 'la', 'rack': '2'}, {'foo': 'bar'}]
|
||||
secondary = Secondary(tag_sets=tag_sets)
|
||||
c = rs_client(
|
||||
maxPoolSize=25,
|
||||
document_class=SON,
|
||||
tz_aware=True,
|
||||
read_preference=secondary,
|
||||
localThresholdMS=77,
|
||||
j=True)
|
||||
|
||||
self.assertEqual(c.max_pool_size, 25)
|
||||
|
||||
for obj in c, c.pymongo_test, c.pymongo_test.test:
|
||||
self.assertEqual(obj.codec_options, CodecOptions(SON, True))
|
||||
self.assertEqual(obj.read_preference, secondary)
|
||||
self.assertEqual(obj.write_concern, WriteConcern(j=True))
|
||||
|
||||
cursor = c.pymongo_test.test.find()
|
||||
self.assertEqual(
|
||||
secondary, cursor._read_preference())
|
||||
|
||||
nearest = Nearest(tag_sets=[{'dc': 'ny'}, {}])
|
||||
cursor = c.pymongo_test.get_collection(
|
||||
"test", read_preference=nearest).find()
|
||||
|
||||
self.assertEqual(nearest, cursor._read_preference())
|
||||
self.assertEqual(c.max_bson_size, 16777216)
|
||||
c.close()
|
||||
|
||||
@client_context.require_secondaries_count(1)
|
||||
def test_timeout_does_not_mark_member_down(self):
|
||||
# If a query times out, the client shouldn't mark the member "down".
|
||||
|
||||
# Disable background refresh.
|
||||
with client_knobs(heartbeat_frequency=999999):
|
||||
c = rs_client(socketTimeoutMS=1000, w=self.w)
|
||||
collection = c.pymongo_test.test
|
||||
collection.insert_one({})
|
||||
|
||||
# Query the primary.
|
||||
self.assertRaises(
|
||||
NetworkTimeout,
|
||||
collection.find_one,
|
||||
{'$where': delay(1.5)})
|
||||
|
||||
self.assertTrue(c.primary)
|
||||
collection.find_one() # No error.
|
||||
|
||||
coll = collection.with_options(
|
||||
read_preference=ReadPreference.SECONDARY)
|
||||
|
||||
# Query the secondary.
|
||||
self.assertRaises(
|
||||
NetworkTimeout,
|
||||
coll.find_one,
|
||||
{'$where': delay(1.5)})
|
||||
|
||||
self.assertTrue(c.secondaries)
|
||||
|
||||
# No error.
|
||||
coll.find_one()
|
||||
|
||||
@client_context.require_ipv6
|
||||
def test_ipv6(self):
|
||||
if client_context.tls:
|
||||
if not HAVE_IPADDRESS:
|
||||
raise SkipTest("Need the ipaddress module to test with SSL")
|
||||
|
||||
port = client_context.port
|
||||
c = rs_client("mongodb://[::1]:%d" % (port,))
|
||||
|
||||
# Client switches to IPv4 once it has first ismaster response.
|
||||
msg = 'discovered primary with IPv4 address "%r"' % (self.primary,)
|
||||
wait_until(lambda: c.primary == self.primary, msg)
|
||||
|
||||
# Same outcome with both IPv4 and IPv6 seeds.
|
||||
c = rs_client("mongodb://[::1]:%d,localhost:%d" % (port, port))
|
||||
|
||||
wait_until(lambda: c.primary == self.primary, msg)
|
||||
|
||||
if client_context.auth_enabled:
|
||||
auth_str = "%s:%s@" % (db_user, db_pwd)
|
||||
else:
|
||||
auth_str = ""
|
||||
|
||||
uri = "mongodb://%slocalhost:%d,[::1]:%d" % (auth_str, port, port)
|
||||
client = rs_client(uri)
|
||||
client.pymongo_test.test.insert_one({"dummy": u"object"})
|
||||
client.pymongo_test_bernie.test.insert_one({"dummy": u"object"})
|
||||
|
||||
dbs = client.list_database_names()
|
||||
self.assertTrue("pymongo_test" in dbs)
|
||||
self.assertTrue("pymongo_test_bernie" in dbs)
|
||||
client.close()
|
||||
|
||||
def _test_kill_cursor_explicit(self, read_pref):
|
||||
with client_knobs(kill_cursor_frequency=0.01):
|
||||
c = rs_client(read_preference=read_pref, w=self.w)
|
||||
db = c.pymongo_test
|
||||
db.drop_collection("test")
|
||||
|
||||
test = db.test
|
||||
test.insert_many([{"i": i} for i in range(20)])
|
||||
|
||||
# Partially evaluate cursor so it's left alive, then kill it
|
||||
cursor = test.find().batch_size(10)
|
||||
next(cursor)
|
||||
self.assertNotEqual(0, cursor.cursor_id)
|
||||
|
||||
if read_pref == ReadPreference.PRIMARY:
|
||||
msg = "Expected cursor's address to be %s, got %s" % (
|
||||
c.primary, cursor.address)
|
||||
|
||||
self.assertEqual(cursor.address, c.primary, msg)
|
||||
else:
|
||||
self.assertNotEqual(
|
||||
cursor.address, c.primary,
|
||||
"Expected cursor's address not to be primary")
|
||||
|
||||
cursor_id = cursor.cursor_id
|
||||
|
||||
# Cursor dead on server - trigger a getMore on the same cursor_id
|
||||
# and check that the server returns an error.
|
||||
cursor2 = cursor.clone()
|
||||
cursor2._Cursor__id = cursor_id
|
||||
|
||||
if sys.platform.startswith('java') or 'PyPy' in sys.version:
|
||||
# Explicitly kill cursor.
|
||||
cursor.close()
|
||||
else:
|
||||
# Implicitly kill it in CPython.
|
||||
del cursor
|
||||
|
||||
time.sleep(5)
|
||||
self.assertRaises(OperationFailure, lambda: list(cursor2))
|
||||
|
||||
def test_kill_cursor_explicit_primary(self):
|
||||
self._test_kill_cursor_explicit(ReadPreference.PRIMARY)
|
||||
|
||||
@client_context.require_secondaries_count(1)
|
||||
def test_kill_cursor_explicit_secondary(self):
|
||||
self._test_kill_cursor_explicit(ReadPreference.SECONDARY)
|
||||
|
||||
@client_context.require_secondaries_count(1)
|
||||
def test_not_master_error(self):
|
||||
secondary_address = one(self.secondaries)
|
||||
direct_client = single_client(*secondary_address)
|
||||
|
||||
with self.assertRaises(NotMasterError):
|
||||
direct_client.pymongo_test.collection.insert_one({})
|
||||
|
||||
db = direct_client.get_database(
|
||||
"pymongo_test", write_concern=WriteConcern(w=0))
|
||||
with self.assertRaises(NotMasterError):
|
||||
db.collection.insert_one({})
|
||||
|
||||
|
||||
class TestReplicaSetWireVersion(MockClientTest):
|
||||
|
||||
@client_context.require_connection
|
||||
@client_context.require_no_auth
|
||||
def test_wire_version(self):
|
||||
c = MockClient(
|
||||
standalones=[],
|
||||
members=['a:1', 'b:2', 'c:3'],
|
||||
mongoses=[],
|
||||
host='a:1',
|
||||
replicaSet='rs',
|
||||
connect=False)
|
||||
self.addCleanup(c.close)
|
||||
|
||||
c.set_wire_version_range('a:1', 3, 7)
|
||||
c.set_wire_version_range('b:2', 2, 3)
|
||||
c.set_wire_version_range('c:3', 3, 4)
|
||||
c.db.command('ismaster') # Connect.
|
||||
|
||||
# A secondary doesn't overlap with us.
|
||||
c.set_wire_version_range('b:2',
|
||||
MAX_SUPPORTED_WIRE_VERSION + 1,
|
||||
MAX_SUPPORTED_WIRE_VERSION + 2)
|
||||
|
||||
def raises_configuration_error():
|
||||
try:
|
||||
c.db.collection.find_one()
|
||||
return False
|
||||
except ConfigurationError:
|
||||
return True
|
||||
|
||||
wait_until(raises_configuration_error,
|
||||
'notice we are incompatible with server')
|
||||
|
||||
self.assertRaises(ConfigurationError, c.db.collection.insert_one, {})
|
||||
|
||||
|
||||
class TestReplicaSetClientInternalIPs(MockClientTest):
|
||||
|
||||
@client_context.require_connection
|
||||
def test_connect_with_internal_ips(self):
|
||||
# Client is passed an IP it can reach, 'a:1', but the RS config
|
||||
# only contains unreachable IPs like 'internal-ip'. PYTHON-608.
|
||||
client = MockClient(
|
||||
standalones=[],
|
||||
members=['a:1'],
|
||||
mongoses=[],
|
||||
ismaster_hosts=['internal-ip:27017'],
|
||||
host='a:1',
|
||||
replicaSet='rs',
|
||||
serverSelectionTimeoutMS=100)
|
||||
self.addCleanup(client.close)
|
||||
with self.assertRaises(AutoReconnect) as context:
|
||||
connected(client)
|
||||
|
||||
self.assertIn("Could not reach any servers in [('internal-ip', 27017)]."
|
||||
" Replica set is configured with internal hostnames or IPs?",
|
||||
str(context.exception))
|
||||
|
||||
class TestReplicaSetClientMaxWriteBatchSize(MockClientTest):
|
||||
|
||||
@client_context.require_connection
|
||||
def test_max_write_batch_size(self):
|
||||
c = MockClient(
|
||||
standalones=[],
|
||||
members=['a:1', 'b:2'],
|
||||
mongoses=[],
|
||||
host='a:1',
|
||||
replicaSet='rs',
|
||||
connect=False)
|
||||
self.addCleanup(c.close)
|
||||
|
||||
c.set_max_write_batch_size('a:1', 1)
|
||||
c.set_max_write_batch_size('b:2', 2)
|
||||
|
||||
# Uses primary's max batch size.
|
||||
self.assertEqual(c.max_write_batch_size, 1)
|
||||
|
||||
# b becomes primary.
|
||||
c.mock_primary = 'b:2'
|
||||
wait_until(lambda: c.max_write_batch_size == 2,
|
||||
'update max_write_batch_size')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Reference in New Issue
Block a user