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

DRIVERS-1003 Introduce CMAP error tests #1369

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
61886cb
wip test
patrickfreed Jan 10, 2023
f0b33d8
wip tests
patrickfreed Jan 11, 2023
fc6544f
more work on tests
patrickfreed Jan 12, 2023
a2e2c1e
add unified cmap tests
patrickfreed Jan 13, 2023
143963c
test finishes
patrickfreed Jan 19, 2023
9187634
error handling prose
patrickfreed Jan 19, 2023
423cca6
error type clarification
patrickfreed Jan 19, 2023
d31abde
some touches
patrickfreed Jan 19, 2023
72f5bbd
eliminate failpoint cmap format operation?
patrickfreed Jan 19, 2023
471b39f
fix connection id in auth test
patrickfreed Jan 20, 2023
88426d0
remove failpoint operation from CMAP format
patrickfreed Jan 20, 2023
91b4d9c
don't ignore events test doesnt expect to see
patrickfreed Jan 20, 2023
55bf35a
add concurrent error test
patrickfreed Jan 20, 2023
0be3db6
combine logging unified tests into one unified directory
patrickfreed Jan 20, 2023
7f9ad15
update checkout prose to mention errors, properly handle
patrickfreed Jan 20, 2023
81e3f94
fix typos
patrickfreed Jan 20, 2023
ccee026
update changelog
patrickfreed Jan 20, 2023
d721af2
add period back in
patrickfreed Jan 20, 2023
4d28ee3
skip tests on replicasets that need a single server
patrickfreed Jan 20, 2023
b1c7f02
fix appname
patrickfreed Jan 20, 2023
dbb8b67
add poolreadyevent to observe events
patrickfreed Feb 2, 2023
aa0d7ae
fix race in test
patrickfreed Feb 2, 2023
bec7969
remove extra connectionId field
patrickfreed Feb 2, 2023
82521a3
Merge branch 'master' into DRIVERS-1003/cmap-establishment-failure-tests
patrickfreed Jun 15, 2023
3c0d6a3
change new cmap-format tests to unified
patrickfreed Jun 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ Before a `Connection <#connection>`_ can be marked as either "available" or "in
must be established. This process involves performing the initial
handshake, handling OP_COMPRESSED, and performing authentication.

If an error is encountered while attempting to establish a connection, it MUST be handled according
to the `Application Errors`_ section in the SDAM specification. After the SDAM machinery has handled the error,
the connection MUST be `closed <#closing-a-connection-internal-implementation>`_. The error can then be propagated
to the context that initiated the connection establishment attempt.

.. code::

try:
Expand All @@ -462,8 +467,11 @@ handshake, handling OP_COMPRESSED, and performing authentication.
emit ConnectionReadyEvent and equivalent log message
return connection
except error:
# Handle error according to SDAM. This may entail clearing the pool.
topology.handle_pre_handshake_error(error)
close connection
throw error # Propagate error in manner idiomatic to language.
# Propagate error in manner idiomatic to language.
throw error


Closing a Connection (Internal Implementation)
Expand Down Expand Up @@ -519,10 +527,6 @@ Populating the pool MUST NOT block any application threads. For example, it
could be performed on a background thread or via the use of non-blocking/async
I/O. Populating the pool MUST NOT be performed unless the pool is "ready".

If an error is encountered while populating a connection, it MUST be handled via
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This notice and associated psuedocode was moved to the "Establishing a Connection" section above to avoid duplication with "Checking out a Connection". The requirement still remains.

the SDAM machinery according to the `Application Errors`_ section in the SDAM
specification.

If minPoolSize is set, the `Connection <#connection>`_ Pool MUST be populated
until it has at least minPoolSize total `Connections <#connection>`_. This MUST
occur only while the pool is "ready". If the pool implements a background
Expand All @@ -544,12 +548,9 @@ established as a result of populating the Pool.

wait until pendingConnectionCount < maxConnecting and pool is "ready"
create connection
try:
establish connection
mark connection as available
except error:
# Defer error handling to SDAM.
topology.handle_pre_handshake_error(error)
try establish connection
mark connection as available
decrement pendingConnectionCount

Checking Out a Connection
-------------------------
Expand All @@ -573,17 +574,26 @@ available `Connection`_ is found or the list of available `Connections

If the list is exhausted, the total number of `Connections <#connection>`_ is
less than maxPoolSize, and pendingConnectionCount < maxConnecting, the pool MUST
create a `Connection`_, establish it, mark it as "in use" and return it. If
totalConnectionCount == maxPoolSize or pendingConnectionCount == maxConnecting,
then the pool MUST wait to service the request until neither of those two
conditions are met or until a `Connection`_ becomes available, re-entering the
checkOut loop in either case. This waiting MUST NOT prevent `Connections
<#connection>`_ from being checked into the pool. Additionally, the Pool MUST
NOT service any newer checkOut requests before fulfilling the original one which
could not be fulfilled. For drivers that implement the WaitQueue via a fair
semaphore, a condition variable may also be needed to to meet this
requirement. Waiting on the condition variable SHOULD also be limited by the
WaitQueueTimeout, if the driver supports one and it was specified by the user.
create a `Connection`_ (as described in `Creating a Connection
<#creating-a-connection-internal-implementation>`_), establish it (as described
in `Establishing a Connection
<#establishing-a-connection-internal-implementation>`_), mark it as "in use" and
return it. If an error is encountered while attempting to establish the
connection, the pool MUST emit a ConnectionCheckOutFailed event with reason
"connectionError" and a corresponding log message before propagating the error
to connection requester.

If the list is exhausted and totalConnectionCount == maxPoolSize or
pendingConnectionCount == maxConnecting, then the pool MUST wait to service the
request until neither of those two conditions are met or until a `Connection`_
becomes available, re-entering the checkOut loop in either case. This waiting
MUST NOT prevent `Connections <#connection>`_ from being checked into the
pool. Additionally, the Pool MUST NOT service any newer checkOut requests before
fulfilling the original one which could not be fulfilled. For drivers that
implement the WaitQueue via a fair semaphore, a condition variable may also be
needed to to meet this requirement. Waiting on the condition variable SHOULD
also be limited by the WaitQueueTimeout, if the driver supports one and it was
specified by the user.

If the pool is "closed" or "paused", any attempt to check out a `Connection
<#connection>`_ MUST throw an Error. The error thrown as a result of the pool
Expand Down Expand Up @@ -654,15 +664,14 @@ Before a given `Connection <#connection>`_ is returned from checkOut, it must be
if connection state is "pending":
try:
establish connection
set connection state to "in use"
decrement pendingConnectionCount
except connection establishment error:
emit ConnectionCheckOutFailedEvent(reason="connectionError") and equivalent log message
decrement totalConnectionCount
throw
finally:
decrement pendingConnectionCount
else:
set connection state to "in use"
decrement availableConnectionCount
set connection state to "in use"
emit ConnectionCheckedOutEvent and equivalent log message
return connection

Expand All @@ -683,6 +692,10 @@ true:

Otherwise, the `Connection <#connection>`_ is marked as available.

If an error is encountered while reading from or writing to a checked out `Connection <#connection>`_, the error MUST
be handled according to the `Application Errors`_ section of the SDAM specification *before* the connection is checked
back into the pool.

.. code::

emit ConnectionCheckedInEvent and equivalent log message
Expand Down Expand Up @@ -1548,6 +1561,7 @@ Changelog
:2022-04-05: Preemptively cancel in progress operations when SDAM heartbeats timeout.
:2022-10-05: Remove spec front matter and reformat changelog.
:2022-10-14: Add connection pool log messages and associated tests.
:2022-01-20: Clarify error handling semantics and add associated tests.

----

Expand Down
9 changes: 1 addition & 8 deletions source/connection-monitoring-and-pooling/tests/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Introduction
Drivers MUST implement all of the following types of CMAP tests:

* Pool unit and integration tests as described in `cmap-format/README.rst <./cmap-format/README.rst>`__
* Integration tests written in the `Unified Test Format <../../unified-test-format/unified-test-format.rst>`_ located in the `/unified <./unified>`_ directory
* Pool prose tests as described below in `Prose Tests`_
* Logging tests as described below in `Logging Tests`_

Prose Tests
===========
Expand All @@ -26,11 +26,4 @@ The following tests have not yet been automated, but MUST still be tested:
#. All ConnectionPoolOptions MUST be the same for all pools created by a MongoClient
#. A user MUST be able to specify all ConnectionPoolOptions via a URI string
#. A user MUST be able to subscribe to Connection Monitoring Events in a manner idiomatic to their language and driver
#. When a check out attempt fails because connection set up throws an error,
assert that a ConnectionCheckOutFailedEvent with reason="connectionError" is emitted.

Logging Tests
=============

Tests for connection pool logging can be found in the `/logging <./logging>`__ subdirectory and are written in the
`Unified Test Format <../../unified-test-format/unified-test-format.rst>`__.
Copy link
Member

Choose a reason for hiding this comment

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

Was this intentionally removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, the logging and new unified tests were combined into a single unified directory, rather than having a separate one for logging.

Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,14 @@ All Unit Tests have some of the following fields:

- ``error``: Indicates that the main thread is expected to error during this test. An error may include of the following fields:

- ``type``: the type of error emitted
- ``type``: the type of error emitted. Currently, only the following error types are supported:

- WaitQueueTimeoutError
- PoolClosedError
- PoolClearedError
- AuthenticationError
- NetworkError

- ``message``: the message associated with that error
- ``address``: Address of pool emitting error

Expand Down Expand Up @@ -126,6 +133,10 @@ the addition of the following fields to each test:
it should be assumed that there is no upper bound on the required server
version.

- ``auth`` (optional): If true, the tests MUST only run if authentication
is enabled. If false, tests MUST only run if authentication is not enabled.
If this field is omitted, there is no authentication requirement.

- ``failPoint``: optional, a document containing a ``configureFailPoint``
command to run against the endpoint being used for the test.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"version": 1,
"style": "integration",
"description": "must properly handle auth error on check out",
"runOn": [
{
"minServerVersion": "4.9.0",
"auth": true
}
],
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"times": 1
},
"data": {
"failCommands": [
"saslContinue"
],
"appName": "poolCheckOutAuthErrorTest",
"errorCode": 18
}
},
"poolOptions": {
"appName": "poolCheckOutAuthErrorTest"
},
"operations": [
{
"name": "ready"
},
{
"name": "checkOut"
}
],
"error": {
"type": "AuthenticationError"
},
"events": [
{
"type": "ConnectionCheckOutStarted",
"address": 42
},
{
"type": "ConnectionCreated",
"connectionId": 1,
"address": 42
},
{
"type": "ConnectionPoolCleared",
"address": 42
},
{
"type": "ConnectionClosed",
"connectionId": 1,
"address": 42,
"reason": "error"
},
{
"type": "ConnectionCheckOutFailed",
"connectionId": 1,
"address": 42,
BorisDog marked this conversation as resolved.
Show resolved Hide resolved
"reason": "connectionError"
}
],
"ignore": [
"ConnectionPoolCreated",
"ConnectionPoolReady"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
version: 1
style: integration
description: must properly handle auth error on check out
runOn:
-
# required for appName in fail point
minServerVersion: "4.9.0"
auth: true
failPoint:
configureFailPoint: failCommand
mode: { times: 1 }
data:
failCommands: ["saslContinue"]
appName: &appName "poolCheckOutAuthErrorTest"
errorCode: 18
poolOptions:
appName: *appName
operations:
- name: ready
- name: checkOut
error:
type: AuthenticationError
events:
- type: ConnectionCheckOutStarted
address: 42
- type: ConnectionCreated
connectionId: 1
address: 42
- type: ConnectionPoolCleared
address: 42
- type: ConnectionClosed
connectionId: 1
address: 42
reason: error
- type: ConnectionCheckOutFailed
connectionId: 1
address: 42
reason: connectionError
ignore:
- ConnectionPoolCreated
- ConnectionPoolReady
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"version": 1,
"style": "integration",
"description": "checking in a connection after checkout fails closes connection",
"runOn": [
{
"minServerVersion": "4.9.0",
"auth": true
}
],
"poolOptions": {
"appName": "poolCheckOutErrorCheckInTest"
},
"failPoint": {
"configureFailPoint": "failCommand",
"mode": {
"skip": 1
},
"data": {
"failCommands": [
"saslContinue"
],
"closeConnection": true,
"appName": "poolCheckOutErrorCheckInTest"
}
},
"operations": [
{
"name": "ready"
},
{
"name": "checkOut",
"label": "conn"
},
{
"name": "start",
"target": "thread1"
},
{
"name": "checkOut",
"thread": "thread1"
},
{
"name": "waitForEvent",
"event": "ConnectionCheckOutFailed",
"count": 1
},
{
"name": "checkIn",
"connection": "conn"
}
],
"events": [
{
"type": "ConnectionCheckOutStarted"
},
{
"type": "ConnectionCreated"
},
{
"type": "ConnectionReady"
},
{
"type": "ConnectionCheckedOut"
},
{
"type": "ConnectionCheckOutStarted"
},
{
"type": "ConnectionCreated"
},
{
"type": "ConnectionPoolCleared"
},
{
"type": "ConnectionClosed",
"reason": "error",
"connectionId": 2
},
{
"type": "ConnectionCheckOutFailed",
"reason": "connectionError"
},
{
"type": "ConnectionCheckedIn"
},
{
"type": "ConnectionClosed",
"connectionId": 1,
"reason": "stale"
}
],
"ignore": [
"ConnectionPoolCreated",
"ConnectionPoolReady"
]
}
Loading