Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PYTHON-2676 Add load balancer tests in EVG #625

Merged
merged 14 commits into from
May 27, 2021

Conversation

ShaneHarvey
Copy link
Member

@ShaneHarvey ShaneHarvey commented May 19, 2021

This PR adds the load balancer test suite. Note that I have removed the "load_balancer/unified" spec tests that depend on connection pinning for cursors+transactions.

@ShaneHarvey

This comment has been minimized.

@ShaneHarvey
Copy link
Member Author

ShaneHarvey commented May 21, 2021

Updated to fix some tests by implementing service_id property on various command events. Then I removed all the test files that depend on connection pinning which I would prefer to implement in a standalone PR. The lone remaining failure is:

 [2021/05/21 22:28:44.460] ----------------------------------------------------------------------
 [2021/05/21 22:28:44.460] Traceback (most recent call last):
 [2021/05/21 22:28:44.460]   File "/data/mci/efdc4de973353c508cc5913a6161c4c0/src/test/unified_format.py", line 1079, in test_case
 [2021/05/21 22:28:44.460]     self.run_scenario(spec)
 [2021/05/21 22:28:44.460]   File "/data/mci/efdc4de973353c508cc5913a6161c4c0/src/test/unified_format.py", line 1063, in run_scenario
 [2021/05/21 22:28:44.460]     self.run_operations(spec['operations'])
 [2021/05/21 22:28:44.460]   File "/data/mci/efdc4de973353c508cc5913a6161c4c0/src/test/unified_format.py", line 997, in run_operations
 [2021/05/21 22:28:44.460]     self.run_special_operation(op)
 [2021/05/21 22:28:44.460]   File "/data/mci/efdc4de973353c508cc5913a6161c4c0/src/test/unified_format.py", line 989, in run_special_operation
 [2021/05/21 22:28:44.460]     method(spec['arguments'])
 [2021/05/21 22:28:44.460]   File "/data/mci/efdc4de973353c508cc5913a6161c4c0/src/test/unified_format.py", line 924, in _testOperation_assertSessionUnpinned
 [2021/05/21 22:28:44.460]     self.assertIsNone(session._transaction.pinned_address)
 [2021/05/21 22:28:44.460] AssertionError: ('127.0.0.1', 8001) is not None

~ I suspect this is a bug in the spec test itself. I'm trying to determine how other drivers pass this test.~
Edit: the bug was that the unified test runner was incorrectly evaluating runOnRequirements. Fixed.

@ShaneHarvey ShaneHarvey marked this pull request as ready for review May 22, 2021 00:07
@ShaneHarvey ShaneHarvey requested a review from prashantmital May 22, 2021 00:08
@ShaneHarvey ShaneHarvey changed the title PYTHON-2672 Add load balancer tests in EVG PYTHON-2676 Add load balancer tests in EVG May 22, 2021
Copy link
Member Author

@ShaneHarvey ShaneHarvey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the failures on latest-non-LB clusters will be fixed by #626

@@ -173,28 +204,28 @@ def __init__(self, test_class):
self._entities = {}
self._listeners = {}
self._session_lsids = {}
self._test_class = test_class
self.test = test_class
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed this from _test_class to test to save horizontal space and avoid needing to break up as many lines.

@@ -142,28 +149,52 @@ def parse_bulk_write_error_result(error):
return parse_bulk_write_result(write_result)


class EventListenerUtil(CommandListener):
class NonLazyCursor(object):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NonLazyCursor is used for the new createFindCursor operation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I follow - why is this needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createFindCursor mandates that the cursor is actually created on the server but not iterated: https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst#createfindcursor

Add loadBalanced=true to default test client opts.
…cking into pool

Fixes:
FAIL: test_sessions_are_reused_in_LB_mode (test_load_balancer.TestUnifiedTransactions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data/mci/941f91603ef9b23e469965893d64cc4e/src/test/unified_format.py", line 1071, in test_case
    self.run_scenario(spec)
  File "/data/mci/941f91603ef9b23e469965893d64cc4e/src/test/unified_format.py", line 1055, in run_scenario
    self.run_operations(spec['operations'])
  File "/data/mci/941f91603ef9b23e469965893d64cc4e/src/test/unified_format.py", line 989, in run_operations
    self.run_special_operation(op)
  File "/data/mci/941f91603ef9b23e469965893d64cc4e/src/test/unified_format.py", line 981, in run_special_operation
    method(spec['arguments'])
  File "/data/mci/941f91603ef9b23e469965893d64cc4e/src/test/unified_format.py", line 934, in _testOperation_assertSameLsidOnLastTwoCommands
    self.assertEqual(*self.__get_last_two_command_lsids(listener))
AssertionError: {'id': Binary(b'\x06\xa6)\xfc\xe5QK/\x80"\xab\xc9]\xb2Tr', 4)} != {'id': Binary(b'\x9c\x86\x19/s\x96O\x85\xa6\x93k\x92d\xff{\x8b', 4)}
- {'id': Binary(b'\x06\xa6)\xfc\xe5QK/\x80"\xab\xc9]\xb2Tr', 4)}
+ {'id': Binary(b'\x9c\x86\x19/s\x96O\x85\xa6\x93k\x92d\xff{\x8b', 4)}
@ShaneHarvey
Copy link
Member Author

Rebased to make the tests green.

@@ -51,6 +51,11 @@ fi
if [ "$SSL" != "nossl" ]; then
export CLIENT_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/client.pem"
export CA_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem"

if [ -n "$TEST_LOADBALANCER" ]; then
export SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI}&tls=true"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't the tls=true be part of the evergreen variable containing the URI instead of having to patch it up here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cribbed this from the Java driver. I think the underlying issue is that MO does not yet add SSL/TLS parameters to the uri it reports: 10gen/mongo-orchestration#287

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

$PYTHON $COVERAGE_ARGS setup.py $C_EXTENSIONS test $TEST_ARGS $OUTPUT

if [ -n "$TEST_LOADBALANCER" ]; then
$PYTHON -m unittest discover -s test/load_balancer -v
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does invoking tests like this still generate the XML output?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Updated to use the unittest-xml-reporting command line tool.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behold:
Screen Shot 2021-05-25 at 10 31 12 AM


def contribute_socket(self, sock_info):
"""Provide socket information to the error handler."""
self.max_wire_version = sock_info.max_wire_version
self.sock_generation = sock_info.generation
self.service_id = sock_info.service_id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is service_id when there is no LB?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's always None when not in LB mode. When in LB mode there is a single connection pool and service_id is used to identify connections to the same host (and differentiate connections to different hosts). Take a peak at #628 and https://github.com/mongodb/specifications/blob/master/source/load-balancers/load-balancers.rst#error-handling for more info.

@@ -142,28 +149,52 @@ def parse_bulk_write_error_result(error):
return parse_bulk_write_result(write_result)


class EventListenerUtil(CommandListener):
class NonLazyCursor(object):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I follow - why is this needed?

@ShaneHarvey ShaneHarvey requested a review from prashantmital May 25, 2021 20:53
DRIVERS_TOOLS=${DRIVERS_TOOLS} MONGODB_URI=${MONGODB_URI} bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh start
- command: expansions.update
params:
file: lb-expansion.yml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How/where is this being used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

platform: ubuntu-18.04
mongodb-version: ["latest"]
auth-ssl: "*"
python-version: ["3.6", "3.9"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the reason for only testing these Pythons? What about pypy? We needn't change it, but a comment with the rationale will be useful and maybe an accompanying ticket if the testing needs to be expanded at a later point.

Copy link
Member Author

@ShaneHarvey ShaneHarvey May 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now it's just for simplicity. I opened: https://jira.mongodb.org/browse/PYTHON-2731

I might just add all python versions while doing that ticket.

@@ -51,6 +51,11 @@ fi
if [ "$SSL" != "nossl" ]; then
export CLIENT_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/client.pem"
export CA_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem"

if [ -n "$TEST_LOADBALANCER" ]; then
export SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI}&tls=true"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

$PYTHON $COVERAGE_ARGS setup.py $C_EXTENSIONS test $TEST_ARGS $OUTPUT

if [ -n "$TEST_LOADBALANCER" ]; then
$PYTHON -m xmlrunner discover -s test/load_balancer -v --locals -o $XUNIT_DIR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does --locals do?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It prints each function's local variables in stack traces: https://docs.python.org/3/library/unittest.html#cmdoption-unittest-locals

So this opaque trace:

FAIL: test_a_connection_can_be_shared_by_a_transaction_and_a_cursor (test_load_balancer.TestUnifiedTransactions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 1087, in test_case
    self.run_scenario(spec)
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 1074, in run_scenario
    self.check_events(spec.get('expectEvents', []))
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 1026, in check_events
    self.match_evaluator.match_event(
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 581, in match_event
    self.test.assertIsInstance(actual, ConnectionCheckedInEvent)
AssertionError: ConnectionReadyEvent(('127.0.0.1', 8001), 2) is not an instance of <class 'pymongo.monitoring.ConnectionCheckedInEvent'>

Becomes much more diagnosable:

FAIL: test_a_connection_can_be_shared_by_a_transaction_and_a_cursor (test_load_balancer.TestUnifiedTransactions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 1087, in test_case
    self.run_scenario(spec)
    self = <test_load_balancer.TestUnifiedTransactions testMethod=test_a_connection_can_be_shared_by_a_transaction_and_a_cursor>
    spec = {'description': 'a connection can be shared by a transaction and a cursor', 'operations': [{'name': 'startTransaction', 'object': 'session0'}, {'name': 'insertOne', 'object': 'collection0', 'arguments': {'document': {'x': 1}, 'session': 'session0'}}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 1}}, {'name': 'createFindCursor', 'object': 'collection0', 'arguments': {'filter': {}, 'batchSize': 2, 'session': 'session0'}, 'saveResultAsEntity': 'cursor0'}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 1}}, {'name': 'close', 'object': 'cursor0'}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 1}}, {'name': 'abortTransaction', 'object': 'session0'}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 0}}], 'expectEvents': [{'client': 'client0', 'events': [{'commandStartedEvent': {'commandName': 'insert'}}, {'commandStartedEvent': {'commandName': 'find'}}, {'commandStartedEvent': {'commandName': 'killCursors'}}, {'commandStartedEvent': {'commandName': 'abortTransaction'}}]}, {'client': 'client0', 'eventType': 'cmap', 'events': [{'connectionReadyEvent': {}}, {'connectionCheckedOutEvent': {}}, {'connectionCheckedInEvent': {}}]}]}
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 1074, in run_scenario
    self.check_events(spec.get('expectEvents', []))
    run_on_spec = []
    self = <test_load_balancer.TestUnifiedTransactions testMethod=test_a_connection_can_be_shared_by_a_transaction_and_a_cursor>
    skip_reason = None
    spec = {'description': 'a connection can be shared by a transaction and a cursor', 'operations': [{'name': 'startTransaction', 'object': 'session0'}, {'name': 'insertOne', 'object': 'collection0', 'arguments': {'document': {'x': 1}, 'session': 'session0'}}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 1}}, {'name': 'createFindCursor', 'object': 'collection0', 'arguments': {'filter': {}, 'batchSize': 2, 'session': 'session0'}, 'saveResultAsEntity': 'cursor0'}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 1}}, {'name': 'close', 'object': 'cursor0'}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 1}}, {'name': 'abortTransaction', 'object': 'session0'}, {'name': 'assertNumberConnectionsCheckedOut', 'object': 'testRunner', 'arguments': {'client': 'client0', 'connections': 0}}], 'expectEvents': [{'client': 'client0', 'events': [{'commandStartedEvent': {'commandName': 'insert'}}, {'commandStartedEvent': {'commandName': 'find'}}, {'commandStartedEvent': {'commandName': 'killCursors'}}, {'commandStartedEvent': {'commandName': 'abortTransaction'}}]}, {'client': 'client0', 'eventType': 'cmap', 'events': [{'connectionReadyEvent': {}}, {'connectionCheckedOutEvent': {}}, {'connectionCheckedInEvent': {}}]}]}
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 1026, in check_events
    self.match_evaluator.match_event(
    actual_events = [ConnectionReadyEvent(('127.0.0.1', 8001), 1), ConnectionCheckedOutEvent(('127.0.0.1', 8001), 1), ConnectionReadyEvent(('127.0.0.1', 8001), 2), ConnectionCheckedOutEvent(('127.0.0.1', 8001), 2), ConnectionCheckedInEvent(('127.0.0.1', 8001), 2), ConnectionCheckedInEvent(('127.0.0.1', 8001), 1)]
    client_name = 'client0'
    event_spec = {'client': 'client0', 'eventType': 'cmap', 'events': [{'connectionReadyEvent': {}}, {'connectionCheckedOutEvent': {}}, {'connectionCheckedInEvent': {}}]}
    event_type = 'cmap'
    events = [{'connectionReadyEvent': {}}, {'connectionCheckedOutEvent': {}}, {'connectionCheckedInEvent': {}}]
    expected_event = {'connectionCheckedInEvent': {}}
    idx = 2
    listener = <test.unified_format.EventListenerUtil object at 0x7fc94603f3a0>
    self = <test_load_balancer.TestUnifiedTransactions testMethod=test_a_connection_can_be_shared_by_a_transaction_and_a_cursor>
    spec = [{'client': 'client0', 'events': [{'commandStartedEvent': {'commandName': 'insert'}}, {'commandStartedEvent': {'commandName': 'find'}}, {'commandStartedEvent': {'commandName': 'killCursors'}}, {'commandStartedEvent': {'commandName': 'abortTransaction'}}]}, {'client': 'client0', 'eventType': 'cmap', 'events': [{'connectionReadyEvent': {}}, {'connectionCheckedOutEvent': {}}, {'connectionCheckedInEvent': {}}]}]
  File "/Users/shane/git/mongo-python-driver/test/unified_format.py", line 581, in match_event
    self.test.assertIsInstance(actual, ConnectionCheckedInEvent)
    actual = ConnectionReadyEvent(('127.0.0.1', 8001), 2)
    event_type = 'cmap'
    expectation = {'connectionCheckedInEvent': {}}
    name = 'connectionCheckedInEvent'
    self = <test.unified_format.MatchEvaluatorUtil object at 0x7fc945968cd0>
    spec = {}
AssertionError: ConnectionReadyEvent(('127.0.0.1', 8001), 2) is not an instance of <class 'pymongo.monitoring.ConnectionCheckedInEvent'>

@ShaneHarvey ShaneHarvey merged commit 93ac5e0 into mongodb:master May 27, 2021
ShaneHarvey added a commit that referenced this pull request May 28, 2021
Add load balancer spec tests
Ensure LB supports retryable reads/writes
Add assertNumberConnectionsCheckedOut, createFindCursor, ignoreResultAndError
Add PoolClearedEvent.service_id and fix isClientError unified test assertion

(cherry picked from commit 93ac5e0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants