PYTHON-2832 Provide options to limit number of mongos servers used in connecting to sharded clusters (#754)

This commit is contained in:
Julius Park 2021-10-26 14:47:51 -07:00 committed by GitHub
parent eabd2235ba
commit bfa5aafb34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 501 additions and 59 deletions

View File

@ -179,6 +179,11 @@ Notable improvements
- Enhanced connection pooling to create connections more efficiently and
avoid connection storms.
- :class:`~pymongo.mongo_client.MongoClient` now accepts a URI and keyword
argument `srvMaxHosts` that limits the number of mongos-like hosts a client
will connect to. More specifically, when a mongodb+srv:// connection string
resolves to more than `srvMaxHosts` number of hosts, the client will randomly
choose a `srvMaxHosts` sized subset of hosts.
Issues Resolved
...............

View File

@ -629,7 +629,8 @@ URI_OPTIONS_VALIDATOR_MAP = {
'w': validate_non_negative_int_or_basestring,
'wtimeoutms': validate_non_negative_integer,
'zlibcompressionlevel': validate_zlib_compression_level,
'srvservicename': validate_string
'srvservicename': validate_string,
'srvmaxhosts': validate_non_negative_integer
}
# Dictionary where keys are the names of URI options specific to pymongo,

View File

@ -652,8 +652,8 @@ class MongoClient(common.BaseObject):
dbase = None
opts = common._CaseInsensitiveDictionary()
fqdn = None
srv_service_name = keyword_opts.get("srvservicename", None)
srv_service_name = keyword_opts.get("srvservicename")
srv_max_hosts = keyword_opts.get("srvmaxhosts")
if len([h for h in host if "/" in h]) > 1:
raise ConfigurationError("host must not contain multiple MongoDB "
"URIs")
@ -669,7 +669,9 @@ class MongoClient(common.BaseObject):
keyword_opts.cased_key("connecttimeoutms"), timeout)
res = uri_parser.parse_uri(
entity, port, validate=True, warn=True, normalize=False,
connect_timeout=timeout, srv_service_name=srv_service_name)
connect_timeout=timeout,
srv_service_name=srv_service_name,
srv_max_hosts=srv_max_hosts)
seeds.update(res["nodelist"])
username = res["username"] or username
password = res["password"] or password
@ -703,6 +705,7 @@ class MongoClient(common.BaseObject):
if srv_service_name is None:
srv_service_name = opts.get("srvServiceName", common.SRV_SERVICE_NAME)
srv_max_hosts = srv_max_hosts or opts.get("srvmaxhosts")
# Handle security-option conflicts in combined options.
opts = _handle_security_options(opts)
# Normalize combined options.
@ -745,6 +748,7 @@ class MongoClient(common.BaseObject):
srv_service_name=srv_service_name,
direct_connection=options.direct_connection,
load_balanced=options.load_balanced,
srv_max_hosts=srv_max_hosts
)
self._topology = Topology(self._topology_settings)

View File

@ -41,7 +41,8 @@ class TopologySettings(object):
fqdn=None,
srv_service_name=common.SRV_SERVICE_NAME,
direct_connection=False,
load_balanced=None):
load_balanced=None,
srv_max_hosts=0):
"""Represent MongoClient's configuration.
Take a list of (host, port) pairs and optional replica set name.
@ -63,7 +64,7 @@ class TopologySettings(object):
self._fqdn = fqdn
self._srv_service_name = srv_service_name
self._heartbeat_frequency = heartbeat_frequency
self._srv_max_hosts = srv_max_hosts or 0
self._direct = direct_connection
self._load_balanced = load_balanced

View File

@ -15,6 +15,7 @@
"""Support for resolving hosts and options from mongodb+srv:// URIs."""
import ipaddress
import random
try:
from dns import resolver
@ -25,7 +26,6 @@ except ImportError:
from pymongo.common import CONNECT_TIMEOUT
from pymongo.errors import ConfigurationError
# dnspython can return bytes or str from various parts
# of its API depending on version. We always want str.
def maybe_decode(text):
@ -48,11 +48,11 @@ _INVALID_HOST_MSG = (
class _SrvResolver(object):
def __init__(self, fqdn,
connect_timeout, srv_service_name):
connect_timeout, srv_service_name, srv_max_hosts=0):
self.__fqdn = fqdn
self.__srv = srv_service_name
self.__connect_timeout = connect_timeout or CONNECT_TIMEOUT
self.__srv_max_hosts = srv_max_hosts or 0
# Validate the fully qualified domain name.
try:
ipaddress.ip_address(fqdn)
@ -111,7 +111,8 @@ class _SrvResolver(object):
raise ConfigurationError("Invalid SRV host: %s" % (node[0],))
if self.__plist != nlist:
raise ConfigurationError("Invalid SRV host: %s" % (node[0],))
if self.__srv_max_hosts:
nodes = random.sample(nodes, min(self.__srv_max_hosts, len(nodes)))
return results, nodes
def get_hosts(self):

View File

@ -15,6 +15,7 @@
"""Represent a deployment of MongoDB servers."""
from collections import namedtuple
from random import sample
from pymongo import common
from pymongo.errors import ConfigurationError
@ -223,6 +224,10 @@ class TopologyDescription(object):
def heartbeat_frequency(self):
return self._topology_settings.heartbeat_frequency
@property
def srv_max_hosts(self):
return self._topology_settings._srv_max_hosts
def apply_selector(self, selector, address, custom_selector=None):
def apply_local_threshold(selection):
@ -446,16 +451,23 @@ def _updated_topology_description_srv_polling(topology_description, seedlist):
if set(sds.keys()) == set(seedlist):
return topology_description
# Add SDs corresponding to servers recently added to the SRV record.
for address in seedlist:
if address not in sds:
sds[address] = ServerDescription(address)
# Remove SDs corresponding to servers no longer part of the SRV record.
for address in list(sds.keys()):
if address not in seedlist:
sds.pop(address)
if topology_description.srv_max_hosts != 0:
new_hosts = set(seedlist) - set(sds.keys())
n_to_add = topology_description.srv_max_hosts - len(sds)
if n_to_add > 0:
seedlist = sample(new_hosts, min(n_to_add, len(new_hosts)))
else:
seedlist = []
# Add SDs corresponding to servers recently added to the SRV record.
for address in seedlist:
if address not in sds:
sds[address] = ServerDescription(address)
return TopologyDescription(
topology_description.topology_type,
sds,

View File

@ -393,7 +393,8 @@ def _check_options(nodes, options):
def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
normalize=True, connect_timeout=None, srv_service_name=None):
normalize=True, connect_timeout=None, srv_service_name=None,
srv_max_hosts=None):
"""Parse and validate a MongoDB URI.
Returns a dict of the form::
@ -494,10 +495,8 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
if opts:
options.update(split_options(opts, validate, warn, normalize))
if srv_service_name is None:
srv_service_name = options.get("srvServiceName", SRV_SERVICE_NAME)
if '@' in host_part:
userinfo, _, hosts = host_part.rpartition('@')
user, passwd = parse_userinfo(userinfo)
@ -510,7 +509,7 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
hosts = unquote_plus(hosts)
fqdn = None
srv_max_hosts = srv_max_hosts or options.get("srvMaxHosts")
if is_srv:
if options.get('directConnection'):
raise ConfigurationError(
@ -529,7 +528,8 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
# Use the connection timeout. connectTimeoutMS passed as a keyword
# argument overrides the same option passed in the connection string.
connect_timeout = connect_timeout or options.get("connectTimeoutMS")
dns_resolver = _SrvResolver(fqdn, connect_timeout, srv_service_name)
dns_resolver = _SrvResolver(fqdn, connect_timeout, srv_service_name,
srv_max_hosts)
nodes = dns_resolver.get_hosts()
dns_options = dns_resolver.get_options()
if dns_options:
@ -542,11 +542,19 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
for opt, val in parsed_dns_options.items():
if opt not in options:
options[opt] = val
if options.get("loadBalanced") and srv_max_hosts:
raise InvalidURI(
"You cannot specify loadBalanced with srvMaxHosts")
if options.get("replicaSet") and srv_max_hosts:
raise InvalidURI("You cannot specify replicaSet with srvMaxHosts")
if "tls" not in options and "ssl" not in options:
options["tls"] = True if validate else 'true'
elif not is_srv and options.get("srvServiceName") is not None:
raise ConfigurationError("The srvServiceName option is only allowed "
"with 'mongodb+srv://' URIs")
elif not is_srv and srv_max_hosts:
raise ConfigurationError("The srvMaxHosts option is only allowed "
"with 'mongodb+srv://' URIs")
else:
nodes = split_hosts(hosts, default_port=default_port)

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test20.test.build.10gen.cc/?srvMaxHosts=1",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because positive integer for srvMaxHosts conflicts with loadBalanced=true (TXT)"
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test3.test.build.10gen.cc/?loadBalanced=true&srvMaxHosts=1",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because positive integer for srvMaxHosts conflicts with loadBalanced=true"
}

View File

@ -0,0 +1,14 @@
{
"uri": "mongodb+srv://test20.test.build.10gen.cc/?srvMaxHosts=0",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
"hosts": [
"localhost.test.build.10gen.cc:27017"
],
"options": {
"loadBalanced": true,
"srvMaxHosts": 0,
"ssl": true
}
}

View File

@ -0,0 +1,14 @@
{
"uri": "mongodb+srv://test3.test.build.10gen.cc/?loadBalanced=true&srvMaxHosts=0",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
"hosts": [
"localhost.test.build.10gen.cc:27017"
],
"options": {
"loadBalanced": true,
"srvMaxHosts": 0,
"ssl": true
}
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test5.test.build.10gen.cc/?srvMaxHosts=1",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because positive integer for srvMaxHosts conflicts with replicaSet option (TXT)"
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=1",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because positive integer for srvMaxHosts conflicts with replicaSet option"
}

View File

@ -0,0 +1,17 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2",
"numSeeds": 2,
"seeds": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"srvMaxHosts": 2,
"ssl": true
}
}

View File

@ -0,0 +1,16 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=3",
"seeds": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"srvMaxHosts": 3,
"ssl": true
}
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=-1",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because srvMaxHosts is not greater than or equal to zero"
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=foo",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because srvMaxHosts is not an integer"
}

View File

@ -0,0 +1,13 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1",
"numSeeds": 1,
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"srvMaxHosts": 1,
"ssl": true
}
}

View File

@ -0,0 +1,17 @@
{
"uri": "mongodb+srv://test5.test.build.10gen.cc/?srvMaxHosts=0",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"authSource": "thisDB",
"replicaSet": "repl0",
"srvMaxHosts": 0,
"ssl": true
}
}

View File

@ -0,0 +1,17 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=0",
"seeds": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"replicaSet": "repl0",
"srvMaxHosts": 0,
"ssl": true
}
}

View File

@ -0,0 +1,16 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2",
"numSeeds": 2,
"seeds": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"hosts": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"options": {
"srvMaxHosts": 2,
"ssl": true
}
}

View File

@ -0,0 +1,15 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=3",
"seeds": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"hosts": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"options": {
"srvMaxHosts": 3,
"ssl": true
}
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=-1",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because srvMaxHosts is not greater than or equal to zero"
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=foo",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because srvMaxHosts is not an integer"
}

View File

@ -0,0 +1,9 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1",
"numSeeds": 1,
"numHosts": 1,
"options": {
"srvMaxHosts": 1,
"ssl": true
}
}

View File

@ -0,0 +1,15 @@
{
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0",
"seeds": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"hosts": [
"localhost.test.build.10gen.cc:27017",
"localhost.test.build.10gen.cc:27018"
],
"options": {
"srvMaxHosts": 0,
"ssl": true
}
}

View File

@ -65,7 +65,7 @@ from pymongo.server_type import SERVER_TYPE
from pymongo.settings import TOPOLOGY_TYPE
from pymongo.srv_resolver import _HAVE_DNSPYTHON
from pymongo.topology import _ErrorContext
from pymongo.topology_description import TopologyDescription
from pymongo.topology_description import TopologyDescription, _updated_topology_description_srv_polling
from pymongo.write_concern import WriteConcern
from test import (client_context,
client_knobs,
@ -1641,6 +1641,20 @@ class TestClient(IntegrationTest):
self.assertEqual(client._topology_settings._srv_service_name,
'customname')
def test_srv_max_hosts_kwarg(self):
client = MongoClient(
'mongodb+srv://test1.test.build.10gen.cc/')
self.assertGreater(
len(client.topology_description.server_descriptions()), 1)
client = MongoClient(
'mongodb+srv://test1.test.build.10gen.cc/', srvmaxhosts=1)
self.assertEqual(
len(client.topology_description.server_descriptions()), 1)
client = MongoClient(
'mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1',
srvmaxhosts=2)
self.assertEqual(
len(client.topology_description.server_descriptions()), 2)
class TestExhaustCursor(IntegrationTest):

View File

@ -50,14 +50,27 @@ class TestDNSLoadBalanced(unittest.TestCase):
pass
class TestDNSSharded(unittest.TestCase):
TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'srv_seedlist', 'sharded')
load_balanced = False
@client_context.require_mongos
def setUp(self):
pass
def create_test(test_case):
def run_test(self):
if not _HAVE_DNSPYTHON:
raise unittest.SkipTest("DNS tests require the dnspython module")
uri = test_case['uri']
seeds = test_case['seeds']
hosts = test_case['hosts']
seeds = test_case.get('seeds')
num_seeds = test_case.get('numSeeds', len(seeds or []))
hosts = test_case.get('hosts')
num_hosts = test_case.get("numHosts", len(hosts or []))
options = test_case.get('options', {})
if 'ssl' in options:
options['tls'] = options.pop('ssl')
@ -75,9 +88,12 @@ def create_test(test_case):
if hosts:
hosts = frozenset(split_hosts(','.join(hosts)))
if seeds:
if seeds or num_seeds:
result = parse_uri(uri, validate=True)
self.assertEqual(sorted(result['nodelist']), sorted(seeds))
if seeds is not None:
self.assertEqual(sorted(result['nodelist']), sorted(seeds))
if num_seeds is not None:
self.assertEqual(len(result['nodelist']), num_seeds)
if options:
opts = result['options']
if 'readpreferencetags' in opts:
@ -106,9 +122,18 @@ def create_test(test_case):
copts['tlsAllowInvalidHostnames'] = True
client = MongoClient(uri, **copts)
wait_until(
lambda: hosts == client.nodes,
'match test hosts to client nodes')
if num_seeds is not None:
self.assertEqual(len(client._topology_settings.seeds),
num_seeds)
if hosts is not None:
wait_until(
lambda: hosts == client.nodes,
'match test hosts to client nodes')
if num_hosts is not None:
wait_until(lambda: num_hosts == len(client.nodes),
"wait to connect to num_hosts")
# XXX: we should block until SRV poller runs at least once
# and re-run these assertions.
else:
try:
parse_uri(uri)
@ -130,6 +155,7 @@ def create_tests(cls):
create_tests(TestDNSRepl)
create_tests(TestDNSLoadBalanced)
create_tests(TestDNSSharded)
class TestParsingErrors(unittest.TestCase):

View File

@ -35,11 +35,11 @@ WAIT_TIME = 0.1
class SrvPollingKnobs(object):
def __init__(self, ttl_time=None, min_srv_rescan_interval=None,
dns_resolver_nodelist_response=None,
nodelist_callback=None,
count_resolver_calls=False):
self.ttl_time = ttl_time
self.min_srv_rescan_interval = min_srv_rescan_interval
self.dns_resolver_nodelist_response = dns_resolver_nodelist_response
self.nodelist_callback = nodelist_callback
self.count_resolver_calls = count_resolver_calls
self.old_min_srv_rescan_interval = None
@ -55,8 +55,8 @@ class SrvPollingKnobs(object):
def mock_get_hosts_and_min_ttl(resolver, *args):
nodes, ttl = self.old_dns_resolver_response(resolver)
if self.dns_resolver_nodelist_response is not None:
nodes = self.dns_resolver_nodelist_response()
if self.nodelist_callback is not None:
nodes = self.nodelist_callback()
if self.ttl_time is not None:
ttl = self.ttl_time
return nodes, ttl
@ -113,7 +113,8 @@ class TestSrvPolling(unittest.TestCase):
if set(expected_nodelist) == set(nodelist):
return True
return False
wait_until(predicate, "see expected nodelist", timeout=100*WAIT_TIME)
wait_until(predicate, "see expected nodelist",
timeout=100*WAIT_TIME)
def assert_nodelist_nochange(self, expected_nodelist, client):
"""Check if the client._topology ever deviates from seeing all nodes
@ -154,7 +155,7 @@ class TestSrvPolling(unittest.TestCase):
self.assert_nodelist_change(self.BASE_SRV_RESPONSE, client)
# Patch list of hosts returned by DNS query.
with SrvPollingKnobs(
dns_resolver_nodelist_response=dns_resolver_response,
nodelist_callback=dns_resolver_response,
count_resolver_calls=count_resolver_calls):
assertion_method(expected_response, client)
@ -207,7 +208,7 @@ class TestSrvPolling(unittest.TestCase):
with SrvPollingKnobs(
ttl_time=WAIT_TIME, min_srv_rescan_interval=WAIT_TIME,
dns_resolver_nodelist_response=initial_callback,
nodelist_callback=initial_callback,
count_resolver_calls=True):
# Client uses unpatched method to get initial nodelist
client = MongoClient(self.CONNECTION_STRING)
@ -216,7 +217,7 @@ class TestSrvPolling(unittest.TestCase):
with SrvPollingKnobs(
ttl_time=WAIT_TIME, min_srv_rescan_interval=WAIT_TIME,
dns_resolver_nodelist_response=final_callback):
nodelist_callback=final_callback):
# Nodelist should reflect new valid DNS resolver response.
self.assert_nodelist_change(response_final, client)
@ -230,6 +231,64 @@ class TestSrvPolling(unittest.TestCase):
raise ConfigurationError
self._test_recover_from_initial(erroring_seedlist)
def test_10_all_dns_selected(self):
response = [("localhost.test.build.10gen.cc", 27017),
("localhost.test.build.10gen.cc", 27019),
("localhost.test.build.10gen.cc", 27020)]
def nodelist_callback():
return response
with SrvPollingKnobs(ttl_time=WAIT_TIME,
min_srv_rescan_interval=WAIT_TIME):
client = MongoClient(self.CONNECTION_STRING, srvMaxHosts=0)
self.addCleanup(client.close)
with SrvPollingKnobs(nodelist_callback=nodelist_callback):
self.assert_nodelist_change(response, client)
def test_11_all_dns_selected(self):
response = [("localhost.test.build.10gen.cc", 27019),
("localhost.test.build.10gen.cc", 27020)]
def nodelist_callback():
return response
with SrvPollingKnobs(
ttl_time=WAIT_TIME, min_srv_rescan_interval=WAIT_TIME):
client = MongoClient(self.CONNECTION_STRING, srvMaxHosts=2)
self.addCleanup(client.close)
with SrvPollingKnobs(nodelist_callback=nodelist_callback):
self.assert_nodelist_change(response, client)
def test_12_new_dns_randomly_selected(self):
response = [("localhost.test.build.10gen.cc", 27020),
("localhost.test.build.10gen.cc", 27019),
("localhost.test.build.10gen.cc", 27017)]
def nodelist_callback():
return response
with SrvPollingKnobs(
ttl_time=WAIT_TIME, min_srv_rescan_interval=WAIT_TIME):
client = MongoClient(self.CONNECTION_STRING, srvMaxHosts=2)
self.addCleanup(client.close)
with SrvPollingKnobs(nodelist_callback=nodelist_callback):
sleep(2*common.MIN_SRV_RESCAN_INTERVAL)
final_topology = set(
client.topology_description.server_descriptions())
self.assertIn(("localhost.test.build.10gen.cc", 27017),
final_topology)
self.assertEqual(len(final_topology), 2)
def test_does_not_flipflop(self):
with SrvPollingKnobs(
ttl_time=WAIT_TIME, min_srv_rescan_interval=WAIT_TIME):
client = MongoClient(self.CONNECTION_STRING, srvMaxHosts=1)
self.addCleanup(client.close)
old = set(client.topology_description.server_descriptions())
sleep(4*WAIT_TIME)
new = set(client.topology_description.server_descriptions())
self.assertSetEqual(old, new)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,116 @@
{
"tests": [
{
"description": "SRV URI with custom srvServiceName",
"uri": "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname",
"valid": true,
"warning": false,
"hosts": null,
"auth": null,
"options": {
"srvServiceName": "customname"
}
},
{
"description": "Non-SRV URI with custom srvServiceName",
"uri": "mongodb://example.com/?srvServiceName=customname",
"valid": false,
"warning": false,
"hosts": null,
"auth": null,
"options": {}
},
{
"description": "SRV URI with srvMaxHosts",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2",
"valid": true,
"warning": false,
"hosts": null,
"auth": null,
"options": {
"srvMaxHosts": 2
}
},
{
"description": "SRV URI with negative integer for srvMaxHosts",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=-1",
"valid": true,
"warning": true,
"hosts": null,
"auth": null,
"options": {}
},
{
"description": "SRV URI with invalid type for srvMaxHosts",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=foo",
"valid": true,
"warning": true,
"hosts": null,
"auth": null,
"options": {}
},
{
"description": "Non-SRV URI with srvMaxHosts",
"uri": "mongodb://example.com/?srvMaxHosts=2",
"valid": false,
"warning": false,
"hosts": null,
"auth": null,
"options": {}
},
{
"description": "SRV URI with positive srvMaxHosts and replicaSet",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&replicaSet=foo",
"valid": false,
"warning": false,
"hosts": null,
"auth": null,
"options": {}
},
{
"description": "SRV URI with positive srvMaxHosts and loadBalanced=true",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=true",
"valid": false,
"warning": false,
"hosts": null,
"auth": null,
"options": {}
},
{
"description": "SRV URI with positive srvMaxHosts and loadBalanced=false",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=false",
"valid": true,
"warning": false,
"hosts": null,
"auth": null,
"options": {
"loadBalanced": false,
"srvMaxHosts": 2
}
},
{
"description": "SRV URI with srvMaxHosts=0 and replicaSet",
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0&replicaSet=foo",
"valid": true,
"warning": false,
"hosts": null,
"auth": null,
"options": {
"replicaSet": "foo",
"srvMaxHosts": 0
}
},
{
"description": "SRV URI with srvMaxHosts=0 and loadBalanced=true",
"uri": "mongodb+srv://test3.test.build.10gen.cc/?srvMaxHosts=0&loadBalanced=true",
"valid": true,
"warning": false,
"hosts": null,
"auth": null,
"options": {
"loadBalanced": true,
"srvMaxHosts": 0
}
}
]
}

View File

@ -1,24 +0,0 @@
{
"tests": [
{
"description": "SRV URI with custom srvServiceName",
"uri": "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname",
"valid": true,
"warning": false,
"hosts": null,
"auth": null,
"options": {
"srvServiceName": "customname"
}
},
{
"description": "Non-SRV URI with custom srvServiceName",
"uri": "mongodb://example.com/?srvServiceName=customname",
"valid": false,
"warning": true,
"hosts": null,
"auth": null,
"options": {}
}
]
}