Skip to content

Commit d2b95d1

Browse files
authored
PYTHON-3336 Test Failure - test_load_balancer failing (#1000)
1 parent b9884f3 commit d2b95d1

File tree

8 files changed

+69
-9
lines changed

8 files changed

+69
-9
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ repos:
5656
rev: 0.11.1
5757
hooks:
5858
- id: doc8
59-
args: [--max-line-length=200]
59+
args: ["--ignore=D001"] # ignore line length
6060
stages: [manual]
6161

6262
- repo: https://github.com/sirosen/check-jsonschema

CONTRIBUTING.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@ branch and submit a `pull request <https://help.github.com/articles/using-pull-r
7070
You might also use the GitHub `Edit <https://github.com/blog/844-forking-with-the-edit-button>`_
7171
button.
7272

73+
Running Tests Locally
74+
---------------------
75+
- Ensure you have started the appropriate Mongo Server(s).
76+
- Run ``python setup.py test`` to run all of the tests.
77+
- Run ``python setup.py test -s test.<mod_name>.<class_name>.<test_name>`` to
78+
run specific tests. You can omit the ``<test_name>`` to test a full class
79+
and the ``<class_name>`` to test a full module. For example:
80+
``python setup.py test -s test.test_change_stream.TestUnifiedChangeStreamsErrors.test_change_stream_errors_on_ElectionInProgress``.
81+
82+
Running Load Balancer Tests Locally
83+
-----------------------------------
84+
- Install ``haproxy`` (available as ``brew install haproxy`` on macOS).
85+
- Clone ``drivers-evergreen-tools``: ``git clone [email protected]:mongodb-labs/drivers-evergreen-tools.git``.
86+
- Start the servers using ``LOAD_BALANCER=true TOPOLOGY=sharded_cluster AUTH=noauth SSL=nossl MONGODB_VERSION=6.0 DRIVERS_TOOLS=./drivers-evergreen-tools MONGO_ORCHESTRATION_HOME=./drivers-evergreen-tools/.evergreen/orchestration ./drivers-evergreen-tools/.evergreen/run-orchestration.sh``.
87+
- Start the load balancer using: ``MONGODB_URI='mongodb://localhost:27017,localhost:27018/' .evergreen/run-load-balancer.sh start``.
88+
- Run the tests using: ``LOADBALANCER=1 TEST_LOADBALANCER=1 SINGLE_MONGOS_LB_URI='mongodb://127.0.0.1:8000/?loadBalanced=true' MULTI_MONGOS_LB_URI='mongodb://127.0.0.1:8001/?loadBalanced=true' MONGODB_URI='mongodb://localhost:27017,localhost:27018/' python setup.py test -s test.test_load_balancer``.
89+
7390
Re-sync Spec Tests
7491
------------------
7592

pymongo/errors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ class ConnectionFailure(PyMongoError):
6161
"""Raised when a connection to the database cannot be made or is lost."""
6262

6363

64+
class WaitQueueTimeoutError(ConnectionFailure):
65+
"""Raised when an operation times out waiting to checkout a connection from the pool.
66+
67+
Subclass of :exc:`~pymongo.errors.ConnectionFailure`.
68+
69+
.. versionadded:: 4.2
70+
"""
71+
72+
6473
class AutoReconnect(ConnectionFailure):
6574
"""Raised when a connection to the database is lost and an attempt to
6675
auto-reconnect will be made.

pymongo/mongo_client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
OperationFailure,
8181
PyMongoError,
8282
ServerSelectionTimeoutError,
83+
WaitQueueTimeoutError,
8384
)
8485
from pymongo.pool import ConnectionClosedReason
8586
from pymongo.read_preferences import ReadPreference, _ServerMode
@@ -1182,6 +1183,7 @@ def _get_socket(self, server, session):
11821183
with _MongoClientErrorHandler(self, server, session) as err_handler:
11831184
# Reuse the pinned connection, if it exists.
11841185
if in_txn and session._pinned_connection:
1186+
err_handler.contribute_socket(session._pinned_connection)
11851187
yield session._pinned_connection
11861188
return
11871189
with server.get_socket(handler=err_handler) as sock_info:
@@ -2064,9 +2066,11 @@ def _add_retryable_write_error(exc, max_wire_version):
20642066
if code in helpers._RETRYABLE_ERROR_CODES:
20652067
exc._add_error_label("RetryableWriteError")
20662068

2067-
# Connection errors are always retryable except NotPrimaryError which is
2069+
# Connection errors are always retryable except NotPrimaryError and WaitQueueTimeoutError which is
20682070
# handled above.
2069-
if isinstance(exc, ConnectionFailure) and not isinstance(exc, NotPrimaryError):
2071+
if isinstance(exc, ConnectionFailure) and not isinstance(
2072+
exc, (NotPrimaryError, WaitQueueTimeoutError)
2073+
):
20702074
exc._add_error_label("RetryableWriteError")
20712075

20722076

pymongo/monitoring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,7 @@ def publish_connection_check_out_failed(self, address, reason):
17741774
event = ConnectionCheckOutFailedEvent(address, reason)
17751775
for subscriber in self.__cmap_listeners:
17761776
try:
1777-
subscriber.connection_check_out_started(event)
1777+
subscriber.connection_check_out_failed(event)
17781778
except Exception:
17791779
_handle_exception()
17801780

pymongo/pool.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
NotPrimaryError,
5353
OperationFailure,
5454
PyMongoError,
55+
WaitQueueTimeoutError,
5556
_CertificateError,
5657
)
5758
from pymongo.hello import Hello, HelloCompat
@@ -1637,7 +1638,7 @@ def _raise_wait_queue_timeout(self) -> NoReturn:
16371638
timeout = _csot.get_timeout() or self.opts.wait_queue_timeout
16381639
if self.opts.load_balanced:
16391640
other_ops = self.active_sockets - self.ncursors - self.ntxns
1640-
raise ConnectionFailure(
1641+
raise WaitQueueTimeoutError(
16411642
"Timeout waiting for connection from the connection pool. "
16421643
"maxPoolSize: %s, connections in use by cursors: %s, "
16431644
"connections in use by transactions: %s, connections in use "
@@ -1650,7 +1651,7 @@ def _raise_wait_queue_timeout(self) -> NoReturn:
16501651
timeout,
16511652
)
16521653
)
1653-
raise ConnectionFailure(
1654+
raise WaitQueueTimeoutError(
16541655
"Timed out while checking out a connection from connection pool. "
16551656
"maxPoolSize: %s, timeout: %s" % (self.opts.max_pool_size, timeout)
16561657
)

test/test_cmap.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@
3838

3939
from bson.objectid import ObjectId
4040
from bson.son import SON
41-
from pymongo.errors import ConnectionFailure, OperationFailure, PyMongoError
41+
from pymongo.errors import (
42+
ConnectionFailure,
43+
OperationFailure,
44+
PyMongoError,
45+
WaitQueueTimeoutError,
46+
)
4247
from pymongo.monitoring import (
4348
ConnectionCheckedInEvent,
4449
ConnectionCheckedOutEvent,
@@ -73,7 +78,7 @@
7378
"ConnectionPoolClosed": PoolClosedEvent,
7479
# Error types.
7580
"PoolClosedError": _PoolClosedError,
76-
"WaitQueueTimeoutError": ConnectionFailure,
81+
"WaitQueueTimeoutError": WaitQueueTimeoutError,
7782
}
7883

7984

test/utils.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,20 @@
3838
from pymongo.cursor import CursorType
3939
from pymongo.errors import ConfigurationError, OperationFailure
4040
from pymongo.hello import HelloCompat
41-
from pymongo.monitoring import _SENSITIVE_COMMANDS
41+
from pymongo.monitoring import (
42+
_SENSITIVE_COMMANDS,
43+
ConnectionCheckedInEvent,
44+
ConnectionCheckedOutEvent,
45+
ConnectionCheckOutFailedEvent,
46+
ConnectionCheckOutStartedEvent,
47+
ConnectionClosedEvent,
48+
ConnectionCreatedEvent,
49+
ConnectionReadyEvent,
50+
PoolClearedEvent,
51+
PoolClosedEvent,
52+
PoolCreatedEvent,
53+
PoolReadyEvent,
54+
)
4255
from pymongo.pool import _CancellationContext, _PoolGeneration
4356
from pymongo.read_concern import ReadConcern
4457
from pymongo.read_preferences import ReadPreference
@@ -81,36 +94,47 @@ def wait_for_event(self, event, count):
8194

8295
class CMAPListener(BaseListener, monitoring.ConnectionPoolListener):
8396
def connection_created(self, event):
97+
assert isinstance(event, ConnectionCreatedEvent)
8498
self.add_event(event)
8599

86100
def connection_ready(self, event):
101+
assert isinstance(event, ConnectionReadyEvent)
87102
self.add_event(event)
88103

89104
def connection_closed(self, event):
105+
assert isinstance(event, ConnectionClosedEvent)
90106
self.add_event(event)
91107

92108
def connection_check_out_started(self, event):
109+
assert isinstance(event, ConnectionCheckOutStartedEvent)
93110
self.add_event(event)
94111

95112
def connection_check_out_failed(self, event):
113+
assert isinstance(event, ConnectionCheckOutFailedEvent)
96114
self.add_event(event)
97115

98116
def connection_checked_out(self, event):
117+
assert isinstance(event, ConnectionCheckedOutEvent)
99118
self.add_event(event)
100119

101120
def connection_checked_in(self, event):
121+
assert isinstance(event, ConnectionCheckedInEvent)
102122
self.add_event(event)
103123

104124
def pool_created(self, event):
125+
assert isinstance(event, PoolCreatedEvent)
105126
self.add_event(event)
106127

107128
def pool_ready(self, event):
129+
assert isinstance(event, PoolReadyEvent)
108130
self.add_event(event)
109131

110132
def pool_cleared(self, event):
133+
assert isinstance(event, PoolClearedEvent)
111134
self.add_event(event)
112135

113136
def pool_closed(self, event):
137+
assert isinstance(event, PoolClosedEvent)
114138
self.add_event(event)
115139

116140

0 commit comments

Comments
 (0)